0
votes

Just for grins I wanted to see if I could rewrite a Muenchian Grouping template in XSLT 1.0 without using <xsl:for-each>. The XML:

<jobs>
  <job>
    <year>2012</year>
    <position>Mayonnaise Maker, Malden Mayonnaise Manufactory, Malden, MA</position>
  </job>
  <job>
    <year>2012</year>
    <position>Twine Twirler, Timmy's Twine, Tyngsboro, MA</position>
  </job>
  <job>
    <year>2013</year>
    <position>Bagel Boiler, Bob's Bagels, Boxboro, MA</position>
  </job>
</jobs>

Standard Muenchian solution, grouping positions by year:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="this-year" match="job" use="year"/>
  <xsl:template match="jobs">
    <resume>
      <xsl:for-each select="job[count(. | key('this-year', year)[1]) = 1]">
        <year y="{year}">
          <xsl:for-each select="key('this-year', year)">
            <position><xsl:value-of select="position"/></position>
          </xsl:for-each>
        </year>
      </xsl:for-each>
    </resume>
  </xsl:template>
</xsl:stylesheet>

Result:

<?xml version="1.0"?>
<resume>
  <year y="2012">
    <position>Mayonnaise Maker, Malden Mayonnaise Manufactory, Malden, MA</position>
    <position>Twine Twirler, Timmy's Twine, Tyngsboro, MA</position>
  </year>
  <year y="2013">
    <position>Bagel Boiler, Bob's Bagels, Boxboro, MA</position>
  </year>
</resume>

It's easy to get rid of the outer <xsl:for-each>. This works:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="this-year" match="job" use="year"/>

  <xsl:template match="jobs">
    <resume>
      <xsl:apply-templates select="job[count(. | key('this-year', year)[1]) = 1]"/>
    </resume>
  </xsl:template>

  <xsl:template match="job">
    <year y="{year}">
      <xsl:for-each select="key('this-year', year)">
        <position><xsl:value-of select="position"/></position>
      </xsl:for-each>
    </year>
  </xsl:template>
</xsl:stylesheet>

This, however...

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:key name="this-year" match="job" use="year"/>

  <xsl:template match="jobs">
    <resume>
      <xsl:apply-templates select="job[count(. | key('this-year', year)[1]) = 1]"/>
    </resume>
  </xsl:template>

  <xsl:template match="job">
    <year y="{year}">
      <xsl:apply-templates select="key('this-year', year)"/>
    </year>
  </xsl:template>

  <xsl:template match="key('this-year', year)">
    <position><xsl:value-of select="position"/></position>
  </xsl:template>

</xsl:stylesheet>

...does not:

error
xsltCompileIdKeyPattern : Literal expected
compilation error: file no-dice.xsl line 17 element template
xsltCompilePattern : failed to compile 'key('this-year', year)'

<xsl:template match="key('this-year', year)"> is valid but it throws that error. Is there some way to push the processing on that key out to another template? Or some other trick to make this work?

1

1 Answers

1
votes

Use a mode:

  <xsl:template match="job">
    <year y="{year}">
      <xsl:apply-templates select="key('this-year', year)" mode="group"/>
    </year>
  </xsl:template>

  <xsl:template match="job" mode="group">
    <position><xsl:value-of select="position"/></position>
  </xsl:template>

Of course, as long as you simply want to copy the position elements you can do that with

  <xsl:template match="job">
    <year y="{year}">
      <xsl:copy-of select="key('this-year', year)/position"/>
    </year>
  </xsl:template>