xslt-2.0xslt-3.0

XSLT: Issue in Copying child nodes to corresponding Parent node


i am trying to write XSLT mapping to move Segments to its corresponding header segment based on key field matches in (key field name ) and (key field name ) segments (MAT=IDEN), If key field not matches then no need to pass segment, i have written XSLT, but its not giving required transformation.

i attached sample input and output as below. Please check.

Input:

<?xml version="1.0" encoding="UTF-8"?>
<HEADER>
    <CODI BEGIN="1">
        <L20 SEGMENT="1">
            <ELN>Value1</ELN>       
            <L24 SEGMENT="1">           
                <MAT>12345</MAT>  
                <field1>text </field1>
                <ITEM SEGMENT="1">
                    <IDEN>12345</IDEN>
                    <Fields>00</Fields>
                </ITEM>
                <ITEM SEGMENT="1">                  
                    <IDEN>45678</IDEN>
                    <Fields>10</Fields>
                </ITEM>
                <ITEM SEGMENT="1">                  
                    <IDEN>45678</IDEN>
                    <Fields>11</Fields>
                </ITEM>
                <ITEM SEGMENT="1">              
                    <IDEN>667788</IDEN>
                    <Fields>12</Fields>
                 </ITEM>
                <ITEM SEGMENT="1">                  
                    <IDEN>667788</IDEN>
                    <Fields>13</Fields>
                </ITEM>
                <ITEM SEGMENT="1">              
                    <IDEN>112233</IDEN>
                    <Fields>20</Fields>
                </ITEM>
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>          
            </L24>
            <L24 SEGMENT="1">               
                <MAT>45678</MAT>
                 <field1>text1 </field1>                
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>          
            </L24>
            
            <L24 SEGMENT="1">               
                <MAT>60987</MAT>    
                  <field1>text2 </field1>           
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
            </L24>
            <L24 SEGMENT="1">               
                <MAT>667788</MAT>   
                <field1>text3 </field1>         
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
                </L24>
            
            <L24 SEGMENT="1">
                <MAT>112233</MAT>   
                  <field1>text4 </field1>           
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
        
            </L24>
            
        </L20>
    </CODI>
</HEADER>

** Desired Output:**

<?xml version="1.0" encoding="UTF-8"?>
<HEADER>
    <CODI BEGIN="1">
        <L20 SEGMENT="1">
            <ELN>Value1</ELN>       
            <L24 SEGMENT="1">           
                <MAT>12345</MAT>
                 <field1>text </field1>
                <ITEM SEGMENT="1">
                    <IDEN>12345</IDEN>
                    <Fields>00</Fields>
                </ITEM>     
            
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>          
            </L24>
            <L24 SEGMENT="1">               
                <MAT>45678</MAT>
               <field1>text1 </field1>              
                    
              <ITEM SEGMENT="1">                    
                    <IDEN>45678</IDEN>
                    <Fields>10</Fields>
                </ITEM>
                <ITEM SEGMENT="1">                  
                    <IDEN>45678</IDEN>
                    <Fields>11</Fields>
                </ITEM> 
                              <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>          
            </L24>
            
            <L24 SEGMENT="1">               
                <MAT>60987</MAT>    
                 <field1>text2 </field1>            
                <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
            </L24>
            <L24 SEGMENT="1">               
                <MAT>667788</MAT>               
                <field1>text3 </field1>     
                    <ITEM SEGMENT="1">              
                    <IDEN>667788</IDEN>
                    <Fields>12</Fields>
                 </ITEM>
                <ITEM SEGMENT="1">                  
                    <IDEN>667788</IDEN>
                    <Fields>13</Fields>
                </ITEM>
                               <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
                </L24>
            
            <L24 SEGMENT="1">
                <MAT>112233</MAT>               
                 <field1>text4 </field1>
                    <ITEM SEGMENT="1">              
                    <IDEN>112233</IDEN>
                    <Fields>20</Fields>
                </ITEM>
                               <L19 SEGMENT="1">
                    <LF>Value2</LF>
                </L19>
        
            </L24>
            
        </L20>
    </CODI>
</HEADER>

** XSLT I used is below:**



<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 
  <xsl:key name="IDEN-by-key" match="//ITEM" use="@IDEN" />
  
  <!-- Identity template to copy all nodes as-is -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="//L24">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    
      <xsl:for-each select="('IDEN-by-key', @IDEN)">
        <xsl:copy-of select="."/>
      </xsl:for-each>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>


Please assist here.


Solution

  • As was indicated in the comments, for your xsl:key you want to use the IDEN element, not an @IDEN attribute. Also, your XSLT was missing key() when attempting to use it.

    Since you want to replace all of the ITEM and substitute with those that match the MAT value, and place those ITEM after the MAT element in your output, I would suggest moving the copy of the ITEM into a template for MAT and creating an empty template for ITEM. Additionally, you don't need to use an xsl:for-each you can just xsl:copy-of and select from the key(), and your match expressions don't need //`:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        
        <xsl:key name="IDEN-by-key" match="ITEM" use="IDEN" />
        
        <!-- Identity template to copy all nodes as-is -->
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
        
        <xsl:template match="MAT">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
            <xsl:copy-of select="key('IDEN-by-key', .)"/>
        </xsl:template>
        
        <xsl:template match="ITEM"/>
        
    </xsl:stylesheet>
    

    Since you have updated the question and changed the inputs, if you want to ensure that the ITEM are produced before the L19 element, change which template you anchor to, but same concept:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output indent="yes"/>
        
        <xsl:key name="IDEN-by-key" match="ITEM" use="IDEN" />
        
        <!-- Identity template to copy all nodes as-is -->
        <xsl:template match="@* | node()">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
        </xsl:template>
        
        <xsl:template match="ITEM"/>
        
        <xsl:template match="L19" >
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
            <xsl:copy-of select="key('IDEN-by-key', ../MAT)"/>
        </xsl:template>
        
    </xsl:stylesheet>