I've started learning XSLT and I've used the Muenchian method in an exercise. I've found 2 different ways of getting my expected result. With the apply-templates
and with an extra for-each
.
The key:
<xsl:key name="tech" match="technology" use="."/>
The first solution using the apply-templates:
<xsl:for-each select="//./technology[generate-id(.)=generate-id(key('tech', .)[1])]">
<team>
<xsl:variable name="selectedTech" select="."/>
<xsl:apply-templates select="../../person[./technology=$selectedTech]">
</team>
</xsl:for-each>
<xsl:template match="person">
<member><xsl:value-of select="name"/></member>
</xsl:template>
The second solution using an additional for-each:
<xsl:for-each select="//./technology[generate-id(.)=generate-id(key('tech', .)[1])]">
<team>
<xsl:variable name="selectedTech" select="."/>
<xsl:for-each select="key('tech', .)">
<member><xsl:value-of select="../name"/></member>
</xsl:for-each>
</team>
</xsl:for-each>
Input is something like this:
<employees>
<person>
<name>Bert</name>
<technology>IBM</technology>
</person>
<person>
<name>Jack</name>
<technology>Microsoft</technology>
</person>
<person>
<name>Karel</name>
<technology>IBM</technology>
</person>
<person>
<name>Bill</name>
<technology>Microsoft</technology>
</person>
<person>
<name>Joris</name>
<technology>OpenSource</technology>
</person>
<person>
<name>Piet</name>
<technology>OpenSource</technology>
</person>
</employees>
Is it better to use a particular solution of these 2? Or which one of these do you recommend and why?
Once you have defined a key and want to access the items in a group it is certainly more efficient to use key('key-name', keyValueExpression)
to do that instead of walking an axis and writing a predicate.
So in my view instead of ../../person[./technology=$selectedTech]
(where I wonder whether it does not need to be ../person[./technology=$selectedTech]
) I would certainly use key('tech', .)
to find the items in a group.
The decision between apply-templates
or for-each
is another question as you can use both.
Generally using apply-templates
and separate templates a stylesheet is better structured and more readable but for quick and short ones for-each might suffice.
For the whole problem I would define the key on person
<xsl:key name="tech" match="person" use="technology"/>
<xsl:for-each select="//person[generate-id(.)=generate-id(key('tech', technology)[1])]">
<team>
<xsl:apply-templates select="key('tech', technology)">
</team>
</xsl:for-each>
<xsl:template match="person">
<member><xsl:value-of select="name"/></member>
</xsl:template>
And of course the first for-each
could also be eliminated using apply-templates
and a mode:
<xsl:key name="tech" match="person" use="technology"/>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="//person[generate-id(.)=generate-id(key('tech', technology)[1])]" mode="team"/>
</xsl:copy>
</xsl:template>
<xsl:template match="person" mode="team">
<team>
<xsl:apply-templates select="key('tech', technology)">
</team>
</xsl:for-each>
<xsl:template match="person">
<member><xsl:value-of select="name"/></member>
</xsl:template>