This is an XSLT 1.0 grouping (Muenchian method) solution, that is mostly in push-style using templates and no <xsl:for-each>
:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kcorpActByID" match="corporationAction"
use="CorporationID"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*">
<xsl:sort select="CorporationID" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match=
"corporationAction|CorporationID
|ActionNotes|DocumentNumber|NumberofPages"/>
<xsl:template match=
"corporationAction
[generate-id()
=
generate-id(key('kcorpActByID', CorporationID)[1])
]">
<corporationAction>
<xsl:copy-of select="CorporationID"/>
<xsl:apply-templates mode="copy"
select="key('kcorpActByID',CorporationID)" />
</corporationAction>
</xsl:template>
<xsl:template match="corporationAction" mode="copy">
<corporationActionDetails>
<xsl:apply-templates/>
</corporationActionDetails>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document:
<corporationActions>
<corporationAction>
<ActionID>5530974</ActionID>
<CorporationID>1044294</CorporationID>
<ActionDate>2009-05-03</ActionDate>
<ActionType>Articles of Organization2</ActionType>
<ActionNotes></ActionNotes>
<DocumentNumber>20110258775-68</DocumentNumber>
<NumberofPages>4</NumberofPages>
</corporationAction>
<corporationAction>
<ActionID>5530975</ActionID>
<CorporationID>1044294</CorporationID>
<ActionDate>2009-05-03</ActionDate>
<ActionType>Miscellaneous</ActionType>
<ActionNotes></ActionNotes>
<DocumentNumber>20110258777-80</DocumentNumber>
<NumberofPages>2</NumberofPages>
</corporationAction>
</corporationActions>
the wanted, correct result is produced:
<corporationActions>
<corporationAction>
<CorporationID>1044294</CorporationID>
<corporationActionDetails>
<ActionID>5530974</ActionID>
<ActionDate>2009-05-03</ActionDate>
<ActionType>Articles of Organization2</ActionType>
</corporationActionDetails>
<corporationActionDetails>
<ActionID>5530975</ActionID>
<ActionDate>2009-05-03</ActionDate>
<ActionType>Miscellaneous</ActionType>
</corporationActionDetails>
</corporationAction>
</corporationActions>
Do note: The added flexibility due to the use and overriding of the identity template. In contrast of <xsl:copy-of select="ActionID|ActionDate|ActionType"/>
we use: <xsl:apply-templates/>
and this assures that any of the children of the current node can be processed in any way we want (and further specify in separate templates).
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:for-each-group select="corporationAction"
group-by="CorporationID">
<xsl:sort select="number(CorporationID)"/>
<corporationAction>
<xsl:sequence select="CorporationID"/>
<xsl:apply-templates mode="copy"
select="current-group()" />
</corporationAction>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="corporationAction" mode="copy">
<corporationActionDetails>
<xsl:apply-templates/>
</corporationActionDetails>
</xsl:template>
<xsl:template match=
"corporationAction|CorporationID
|ActionNotes|DocumentNumber|NumberofPages"/>
</xsl:stylesheet>
Do note: the simplified grouping using <xsl:for-each-group>
and the current-group()
function.