Search code examples
xsltmuenchian-groupingapply-templates

XSLT using extra for-each or apply-templates using Muenchian method?


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?


Solution

  • 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>