The following stylesheet shows a general approach to grouping at several levels:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:key name="byRegion" match="Row" use="@region" /> <xsl:key name="byRegionState" match="Row" use="concat(@region, '|', @state)" /> <xsl:key name="byRegionStateDept" match="Row" use="concat(@region, '|', @state, '|', @dept)" /> <xsl:template match="Row[generate-id() = generate-id(key('byRegion', @region)[1])]"> <region name="{@region}"> <xsl:apply-templates select="key('byRegion', @region) [generate-id() = generate-id(key('byRegionState', concat(@region, '|', @state))[1])]" mode="states" /> </region> </xsl:template> <xsl:template match="Row" mode="states"> <state name="{@state}"> <xsl:apply-templates select="key('byRegionState', concat(@region, '|', @state)) [generate-id() = generate-id(key('byRegionStateDept', concat(@region, '|', @state, '|', @dept))[1])]" mode="dept" /> </state> </xsl:template> <xsl:template match="Row" mode="dept"> <dept><xsl:value-of select="@dept" /></dept> </xsl:template> </xsl:stylesheet>
It outputs the following result to your conclusion:
<region name="East"> <state name="NY"> <dept>HR</dept> <dept>SD</dept> <dept>MM</dept> </state> <state name="NJ"> <dept>HR</dept> <dept>SD</dept> </state> </region> <region name="West"> <state name="CO"> <dept>SD</dept> <dept>MM</dept> </state> </region>
source share