Search code examples
xpathxslt-2.0muenchian-grouping

Conditional grouping in XSLT


Request your help to transform XML using XSLT 2

My input XML looks like below.

    <?xml version="1.0" encoding="UTF-8"?>
<Workers>
    <Worker>
        <Batch_number>1234</Batch_number>
        <Name>David</Name>
        <Assignment>
            <type>A</type>
            <name>New York</name>
        </Assignment>
    </Worker>
    <Worker>
        <Batch_number>1234</Batch_number>
        <Name>David</Name>
        <Assignment>
            <type>B</type>
            <name>Boston</name>
        </Assignment>
    </Worker>
    <Worker>
        <Batch_number>1234</Batch_number>
        <Name>David</Name>
        <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
    </Worker>
    <Worker>
        <Batch_number>78698</Batch_number>
        <Name>Karen</Name>
        <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
    </Worker>
</Workers>

Desired output looks like below.

<?xml version="1.0" encoding="UTF-8"?>
<Worker>
    <Batch_number>1234</Batch_number>
    <Name>David</Name>
    <Assignment>
            <type>A</type>
            <name>New York</name>
        </Assignment>
    <Assignment>
            <type>B</type>
            <name>Boston</name>
        </Assignment>
    <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
</Worker>
<Worker>
    <Batch_number>78698</Batch_number>
    <Name>Karen</Name>
    <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
</Worker>

Condition for transformation is:

If child node <Worker>with same <Batch_number>exists in input file, all Assignment nodes for same <Batch_number> should be grouped together under one Worker child node right after <Batch_number> and <Name>

Can someone help me write the correct code to achieve this please?


Solution

  • Although your desired output XML is not well-formed, here is an XSLT-2.0 solution which suffices your special needs:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="xml" indent="yes"/>
    
        <xsl:template match="/Workers">
            <xsl:for-each-group select="Worker" group-by="Batch_number">
                <Worker>
                    <Batch_number><xsl:value-of select="current-grouping-key()" /></Batch_number>
                    <Name><xsl:value-of select="current()/Name" /></Name>
                    <xsl:copy-of select="current-group()/Assignment" />
                </Worker>
            </xsl:for-each-group>
        </xsl:template>
    
    </xsl:stylesheet>
    

    Its output is:

    <?xml version="1.0" encoding="UTF-8"?>
    <Worker>
        <Batch_number>1234</Batch_number>
        <Name>David</Name>
        <Assignment>
            <type>A</type>
            <name>New York</name>
        </Assignment>
        <Assignment>
            <type>B</type>
            <name>Boston</name>
        </Assignment>
        <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
    </Worker>
    <Worker>
        <Batch_number>78698</Batch_number>
        <Name>Karen</Name>
        <Assignment>
            <type>C</type>
            <name>Chicago</name>
        </Assignment>
    </Worker>