Harshit Singh
Harshit Singh

Reputation: 17

XSLT 3.0 Combine nodes into single parent node


I have a sample XML to which I need to transform to combine nodes with the same Archived_Employee_ID. The XSLT which I created already works but I'm looking for a more clean solution which is high performing using XSLT3.0 since I'm expecting huge data load.

XSLT I'm using:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wd="ur">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:template match="wd:Report_Data">
        <wd:Report_Data>
            <xsl:for-each-group select="wd:Report_Entry" group-by="wd:Archived_Employee_ID">
                <wd:Report_entry>
                <wd:Archived_Employee_ID>
                    <xsl:value-of select="wd:Archived_Employee_ID"/>
                </wd:Archived_Employee_ID>
                <xsl:call-template name="Fix"/>
                </wd:Report_entry>
            </xsl:for-each-group>
        </wd:Report_Data>
    </xsl:template>

    <xsl:template name="Fix">
        <wd:AE_group>
            <xsl:variable name="AE">
                <xsl:value-of select="distinct-values(current-group()/wd:Archived_Expectation)"
                    separator="§"/>
            </xsl:variable>
            <xsl:for-each select="tokenize($AE, '§')">
                <wd:Archived_Expectation>
                    <xsl:value-of select="."/>
                </wd:Archived_Expectation>
            </xsl:for-each>
        </wd:AE_group>
        <wd:AE_Category_Group>
            <xsl:variable name="Category">
                <xsl:value-of select="(current-group()/wd:Archived_Expectation_Category)"
                    separator="§"/>
            </xsl:variable>
            <xsl:for-each select="tokenize($Category, '§')">
                <wd:Archived_Expectation_Category>>
                    <xsl:value-of select="."/>
                </wd:Archived_Expectation_Category>>
            </xsl:for-each>
        </wd:AE_Category_Group>
    </xsl:template>
</xsl:stylesheet>

Here is sample XML:

 <?xml version='1.0' encoding='UTF-8'?>
<wd:Report_Data xmlns:wd="ur">
   <wd:Report_Entry>
      <wd:Archived_Employee_ID>XYZ</wd:Archived_Employee_ID>
      <wd:Archived_Expectation>Increase complaint statistics.</wd:Archived_Expectation>
      <wd:Archived_Expectation_Category>Roles, Responsibilities &amp;
         Competencies</wd:Archived_Expectation_Category>
   </wd:Report_Entry>
   <wd:Report_Entry>
      <wd:Archived_Employee_ID>XYZ</wd:Archived_Employee_ID>
      <wd:Archived_Expectation>Verantwortungsbereich</wd:Archived_Expectation>
      <wd:Archived_Expectation_Category> Competencies</wd:Archived_Expectation_Category>
   </wd:Report_Entry>
   <wd:Report_Entry>
      <wd:Archived_Employee_ID>123</wd:Archived_Employee_ID>
      <wd:Archived_Expectation>Verantwortungsbereich: 5%</wd:Archived_Expectation>
      <wd:Archived_Expectation_Category> Role</wd:Archived_Expectation_Category>
   </wd:Report_Entry>
</wd:Report_Data>

Expected Output:

<wd:Report_Data xmlns:wd="ur">
   <wd:Report_entry>
      <wd:Archived_Employee_ID>XYZ</wd:Archived_Employee_ID>
      <wd:AE_group>
         <wd:Archived_Expectation>Increase complaint statistics.</wd:Archived_Expectation>
         <wd:Archived_Expectation>Verantwortungsbereich</wd:Archived_Expectation>
      </wd:AE_group>
      <wd:AE_Category_Group><wd:Archived_Expectation_Category>Roles, Responsibilities &amp;
            Competencies</wd:Archived_Expectation_Category>
            <wd:Archived_Expectation_Category>
         Competencies</wd:Archived_Expectation_Category></wd:AE_Category_Group>
   </wd:Report_entry>
   <wd:Report_entry>
      <wd:Archived_Employee_ID>123</wd:Archived_Employee_ID>
      <wd:AE_group>
         <wd:Archived_Expectation>Verantwortungsbereich: 5%</wd:Archived_Expectation>
      </wd:AE_group>
      <wd:AE_Category_Group><wd:Archived_Expectation_Category>
            Role</wd:Archived_Expectation_Category> </wd:AE_Category_Group>
   </wd:Report_entry>
</wd:Report_Data>

Upvotes: 0

Views: 310

Answers (2)

Martin Honnen
Martin Honnen

Reputation: 167471

There is not much you can cleanly and purely stream here as you want to use for-each-group group-by with a group-by expression selecting a child element (which is not "motionless") so the only way is to use copy-of() in the grouping population

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xpath-default-namespace="ur" xmlns:wd="ur">

    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:mode on-no-match="shallow-copy" streamable="yes"/>

    <xsl:template match="Report_Data">
        <xsl:copy>
            <xsl:for-each-group select="Report_Entry!copy-of()" group-by="Archived_Employee_ID">
                <xsl:copy>
                    <xsl:copy-of select="Archived_Employee_ID"/>
                    <wd:AE_group>
                        <xsl:for-each-group select="current-group()/Archived_Expectation" group-by=".">
                            <xsl:copy-of select="." />
                        </xsl:for-each-group>
                    </wd:AE_group>
                    <wd:AE_Category_Group>
                        <xsl:for-each-group select="current-group()/Archived_Expectation_Category" group-by=".">
                            <xsl:copy-of select="." />
                        </xsl:for-each-group>
                    </wd:AE_Category_Group>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Tomalak
Tomalak

Reputation: 338148

Since you're effectively grouping in your <xsl:template name="Fix">, I don't understand why you're not using for-each-group

<xsl:template name="Fix">
    <wd:AE_group>
        <xsl:for-each-group select="current-group()/wd:Archived_Expectation" group-by=".">
            <xsl:copy-of select="." />
        </xsl:for-each-group>
    </wd:AE_group>
    <wd:AE_Category_Group>
        <xsl:for-each-group select="current-group()/wd:Archived_Expectation_Category" group-by=".">
            <xsl:copy-of select="." />
        </xsl:for-each-group>
    </wd:AE_Category_Group>
</xsl:template>

You could also loop over distinct-values() if you prefer (I wouldn't):

<xsl:for-each select="distinct-values(current-group()/wd:Archived_Expectation)">
    <wd:Archived_Expectation>
        <xsl:copy-of select="." />
    </wd:Archived_Expectation>
</xsl:for-each>

Upvotes: 2

Related Questions