This solution is a little bit simpler, more efficient and at the same time more general than the one presented by Richard:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- -->
<xsl:key name="kFileByVal" match="File"
use="." />
<!-- -->
<xsl:key name="kDescByFile" match="Description"
use="../File"/>
<!-- -->
<xsl:template match="/*">
<html>
<body>
<xsl:for-each select=
"*/File[generate-id()
=
generate-id(key('kFileByVal',.)[1])]">
<h1><xsl:value-of select="."/></h1>
<xsl:for-each select="key('kDescByFile', .)">
<p><xsl:value-of select="."/></p>
</xsl:for-each>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML document:
<Problems>
<Problem>
<File>file1</File>
<Description>desc1</Description>
</Problem>
<Problem>
<File>file1</File>
<Description>desc2</Description>
</Problem>
<Problem>
<File>file2</File>
<Description>desc1</Description>
</Problem>
</Problems>
Produces the wanted result:
<html>
<body>
<h1>file1</h1>
<p>desc1</p>
<p>desc2</p>
<h1>file2</h1>
<p>desc1</p>
</body>
</html>
Do note the simple match pattern of the first <xsl:key>
and how, using a second <xsl:key>
, we locate all "Description
" elements that are siblings of a "File
" element that has a given value.
We could have used more templates instead of <xsl:for-each>
pull-processing, however this is a quite simple case and the solution really benefits from shorter, more compact and more readable code.
Also note, that in XSLT 2.0 one will typically use the <xsl:for-each-group>
instruction instead of the Muenchian method.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…