Search code examples
xmlxslt

Issue with XSLT Code while creating new XML Structure


I am trying to write an XSLT for below requirement, where

/parent/queryCompoundEmployeeResponse/CompoundEmployee/person/person_id_external = /parent/row/PERSON_ID_EXTERNAL 

then contents of /parent/row should be cut and pasted under corresponding CompoundEmployee.

Also the <parent> tag should be removed from ouput and all <CompoundEmployee> should be under <queryCompoundEmployeeResponse>.

I have tried below mentioned code, it does not return output as expected. Request your help.

Input

<?xml version='1.0' encoding='UTF-8'?>
<parent>
    <queryCompoundEmployeeResponse>
        <CompoundEmployee>
            <person>
                <person_id_external>100007</person_id_external>
            </person>
        </CompoundEmployee>
        <CompoundEmployee>
            <person>
                <person_id_external>100008</person_id_external>
            </person>
        </CompoundEmployee>
    </queryCompoundEmployeeResponse>
    <row>
        <PERSON_ID_EXTERNAL>100008</PERSON_ID_EXTERNAL>
        <APPLICANT_NUMBER>34</APPLICANT_NUMBER>
    </row>
    <row>
        <PERSON_ID_EXTERNAL>100007</PERSON_ID_EXTERNAL>
        <APPLICANT_NUMBER>12</APPLICANT_NUMBER>
    </row>
</parent>

Output:

    <?xml version='1.0' encoding='UTF-8'?>
        <queryCompoundEmployeeResponse>
            <CompoundEmployee>
                <person>
                    <person_id_external>100007</person_id_external>
                </person>
                <row>
                    <PERSON_ID_EXTERNAL>100007</PERSON_ID_EXTERNAL>
                    <APPLICANT_NUMBER>12</APPLICANT_NUMBER>
                </row>
            </CompoundEmployee>
            <CompoundEmployee>
                <person>
                    <person_id_external>100008</person_id_external>
                </person>
            </CompoundEmployee>
                <row>
            <PERSON_ID_EXTERNAL>100008</PERSON_ID_EXTERNAL>
            <APPLICANT_NUMBER>34</APPLICANT_NUMBER>
        </row>
        </queryCompoundEmployeeResponse>

Current Code

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    
    <!-- Identity template copies everything as is -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Matches rows with PERSON_ID_EXTERNAL and moves them under corresponding CompoundEmployee -->
    <xsl:template match="parent/row">
        <xsl:variable name="currentId" select="PERSON_ID_EXTERNAL"/>
        <xsl:variable name="targetCompoundEmployee" select="//CompoundEmployee[person/person_id_external = $currentId]"/>
        
        <!-- Copy row content under corresponding CompoundEmployee -->
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
        
        <!-- Remove the row after moving it -->
        <xsl:apply-templates select="//row[PERSON_ID_EXTERNAL = $currentId]/following-sibling::row[1]"/>
        
        <!-- Remove empty parent tag if there are no more rows -->
        <xsl:if test="not(//row)">
            <xsl:apply-templates select="parent::node()[not(self::row)]"/>
        </xsl:if>
    </xsl:template>
    
    <!-- Matches parent and moves CompoundEmployee under queryCompoundEmployeeResponse -->
    <xsl:template match="parent">
        <xsl:apply-templates select="queryCompoundEmployeeResponse"/>
    </xsl:template>
    
</xsl:stylesheet>

Solution

  • The output you show does not match your description.

    I think you want to do:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:key name="rows" match="row" use="PERSON_ID_EXTERNAL" />
        
    <xsl:template match="/parent">
        <queryCompoundEmployeeResponse>
            <xsl:for-each select="queryCompoundEmployeeResponse/CompoundEmployee">
                <xsl:copy>
                    <xsl:copy-of select="person"/>
                    <xsl:copy-of select="key('rows', person/person_id_external)"/>
                </xsl:copy>
            </xsl:for-each>
        </queryCompoundEmployeeResponse>
    </xsl:template>
        
    </xsl:stylesheet>
    

    to return:

    <?xml version="1.0" encoding="utf-8"?>
    <queryCompoundEmployeeResponse>
       <CompoundEmployee>
          <person>
             <person_id_external>100007</person_id_external>
          </person>
          <row>
             <PERSON_ID_EXTERNAL>100007</PERSON_ID_EXTERNAL>
             <APPLICANT_NUMBER>12</APPLICANT_NUMBER>
          </row>
       </CompoundEmployee>
       <CompoundEmployee>
          <person>
             <person_id_external>100008</person_id_external>
          </person>
          <row>
             <PERSON_ID_EXTERNAL>100008</PERSON_ID_EXTERNAL>
             <APPLICANT_NUMBER>34</APPLICANT_NUMBER>
          </row>
       </CompoundEmployee>
    </queryCompoundEmployeeResponse>