xsltxslt-2.0xslt-3.0xslt-grouping

XSLT 3.0 Transformation


Trying to transform xml and group nodes by value and elements in xslt 3.0

Hey Guys, I have got an input xml as below

<?xml version='1.0' encoding='UTF-8'?>
<Applications>
    <Application>
        <applicationId>280</applicationId>
        <cust_IDCheckIDCollation>
            <Option>
                <id>197249</id>
            </Option>
        </cust_IDCheckIDCollation>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>292</applicationId>
        <cust_IDCheckIDCollation>
            <Option>
                <id>197249</id>
            </Option>
        </cust_IDCheckIDCollation>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>280</applicationId>
        <cust_OnlineReferenceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_OnlineReferenceCheck>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>292</applicationId>
        <cust_OnlineReferenceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_OnlineReferenceCheck>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>280</applicationId>
        <cust_AustralianWorkRights>
            <Option>
                <id>197250</id>
            </Option>
        </cust_AustralianWorkRights>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>292</applicationId>
        <cust_AustralianWorkRights>
            <Option>
                <id>197250</id>
            </Option>
        </cust_AustralianWorkRights>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>280</applicationId>
        <cust_NationalPoliceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_NationalPoliceCheck>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
    <Application>
        <applicationId>292</applicationId>
        <cust_NationalPoliceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_NationalPoliceCheck>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
    </Application>
</Applications>

I need to group it by applicationId and then sub group it by element name to remove duplicate elements.

Output Expected

<Applications>
    <Application>
        <applicationId>280</applicationId>
        <cust_IDCheckIDCollation>
            <Option>
                <id>197249</id>
            </Option>
        </cust_IDCheckIDCollation>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
        <cust_OnlineReferenceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_OnlineReferenceCheck>
        <cust_AustralianWorkRights>
            <Option>
                <id>197250</id>
            </Option>
        </cust_AustralianWorkRights>
        <cust_NationalPoliceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_NationalPoliceCheck>
    </Application>
    <Application>
        <applicationId>292</applicationId>
        <cust_IDCheckIDCollation>
            <Option>
                <id>197249</id>
            </Option>
        </cust_IDCheckIDCollation>
        <cust_overallduedilligencestatus>
            <Option>
                <id>197276</id>
            </Option>
        </cust_overallduedilligencestatus>
        <cust_OnlineReferenceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_OnlineReferenceCheck>
        <cust_AustralianWorkRights>
            <Option>
                <id>197250</id>
            </Option>
        </cust_AustralianWorkRights>
        <cust_NationalPoliceCheck>
            <Option>
                <id>197249</id>
            </Option>
        </cust_NationalPoliceCheck>
    </Application>
    <Applications>

Is there an easier way to do this in xslt 3.0.

I had written the below in xslt 2.0 but it isn't returning the expected result.

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    <xsl:template match="/Applications">
        <xsl:copy>
            <xsl:for-each-group select="Application" group-by="applicationId">
                <xsl:copy>
                    <xsl:for-each-group select="current-group()/*" group-by="name()">
                        <xsl:element name="{current-grouping-key()}">
                            <xsl:value-of select="(current-group())[1]"/>
                        </xsl:element>
                    </xsl:for-each-group>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>```

Solution

  • I think you want to do:

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:template match="/Applications">
        <xsl:copy>
            <xsl:for-each-group select="Application" group-by="applicationId">
                <xsl:copy>
                    <xsl:copy-of select="applicationId"/>
                    <xsl:for-each-group select="current-group()/(* except applicationId)" group-by="name()">
                        <xsl:copy-of select="current-group()[1]"/>
                    </xsl:for-each-group>
                </xsl:copy>
            </xsl:for-each-group>
        </xsl:copy>
    </xsl:template>
        
    </xsl:stylesheet>
    

    I don't know of anything that would be different in in XSLT 3.0.