XSLT group by value
I have XML document
<Contracts>
<Cont开发者_开发技巧ract>
<F1>key1</F1>
<F2>2345</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key1</F1>
<F2>34562</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>41575</F2>
<F3>val</F3>
<F4>bal</F4>
</Contract>
</Contracts>
I need group these contracts by Contracts/Contract/F1
(key1, key2) to get this result:
<Contracts>
<Set>
<Contract>
<F1>key1</F1>
<F2>2345</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key1</F1>
<F2>34562</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Set>
<Set>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
</Set>
</Contracts>
How can I do it with XSLT ?
I. XSLT 1.0 solution (Muenchian grouping, 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="kContrByF1" match="Contract" use="F1"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"Contract[not(generate-id()
=
generate-id(key('kContrByF1', F1)[1])
)
]"/>
<xsl:template match="Contract">
<Contract>
<Set>
<xsl:copy-of select="key('kContrByF1', F1)"/>
</Set>
</Contract>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document:
<Contracts>
<Contract>
<F1>key1</F1>
<F2>2345</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key1</F1>
<F2>34562</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>41575</F2>
<F3>val</F3>
<F4>bal</F4>
</Contract>
</Contracts>
the wanted, correct result is produced:
<Contracts>
<Contract>
<Set>
<Contract>
<F1>key1</F1>
<F2>2345</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
<Contract>
<F1>key1</F1>
<F2>34562</F2>
<F3>val</F3>
<F4>dal</F4>
</Contract>
</Set>
</Contract>
<Contract>
<Set>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>416235</F2>
<F3>val</F3>
<F4>sal</F4>
</Contract>
<Contract>
<F1>key2</F1>
<F2>41575</F2>
<F3>val</F3>
<F4>bal</F4>
</Contract>
</Set>
</Contract>
</Contracts>
II. XSLT 2.0 solution:
<xsl:template match="/*">
<Contracts>
<xsl:for-each-group select="Contract" group-by="F1">
<Contract>
<Set>
<xsl:sequence select="current-group()"/>
</Set>
</Contract>
</xsl:for-each-group>
</Contracts>
</xsl:template>
</xsl:stylesheet>
Again, when this transformation is applied on the same XML document (above) the same correct result is produced.
Do note:
The use of the
xsl:for-each-group
instruction.The use of the
current-group()
function.
Use an XSL key and Muenchian grouping.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kContract" match="Contract" use="F1" />
<xsl:template match="Contracts">
<xsl:copy>
<xsl:for-each select="
Contract[
generate-id() = generate-id(key('kContract', F1)[1])
]
">
<Set>
<xsl:copy-of select="key('kContract', F1)" />
</Set>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This stylesheet produces exactly the output you want.
精彩评论