Search code examples
xslt-1.0xpath-1.0

Accessing current node in predicate with xsl:for-each


I'm stuck finding out how I can access the current node whilst iterating over a collection of nodes with xsL:for-each. This is my XML:

<events>
    <event>
        <date>
            <year>2012</year>
            <month>July</month>
        </date>
        <description>...</description>
    </event>
    <!-- more events -->
</events>

What I try to achieve is an HTML-representation like this:

<h2>2012</h2>
<dl>
    <dt>July</dt>
    <dd>One of these for every event with year=2012 and month=July</dd>

    <dt>August</dt>
    <!-- ... -->
</dl>
<h2>2013</h2>
<!-- ... -->

I'm using an XPath-expression to get all distinct years and then iterate over them calling a template called year with parameters $year and $events. Getting the value for $year is easy, but I'm struggling finding the right events. The following won't work, probably because . in the second predicate refers to the event being tested for the predicate. But how to access the year in there?

<xsl:template match="events">
<xsl:for-each select="event[not(date/year=preceding-sibling::event/date/year)]/date/year">
<xsl:call-template name="year">
    <xsl:with-param name="year" select="." />
    <xsl:with-param name="events" select="event[date/year=.]" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>

Many thanks in advance!

PS: Must work with XPath and XSLT 1.0.


Solution

  • This transformation:

    <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="kYear" match="year" use="."/>
    
     <xsl:key name="kEventByMonthYear" match="event"
      use="string(date)"/>
    
     <xsl:key name="kMonthByMonthYear" match="month"
      use="string(..)"/>
    
     <xsl:key name="kMonthByYear" match="month"
     use="../year"/>
    
     <xsl:template match="/*">
      <xsl:for-each select=
        "*/date/year
                [generate-id() = generate-id(key('kYear', .)[1])]
        ">
       <h2><xsl:value-of select="."/></h2>
       <dl>
          <xsl:apply-templates select=
          "key('kMonthByYear', current())
               [generate-id()
               =
                generate-id(key('kMonthByMonthYear',
                                 string(..)
                             )[1]
                         )
                ]"/>
          </dl>
      </xsl:for-each>
     </xsl:template>
    
     <xsl:template match="month">
        <dt><xsl:value-of select="."/></dt>
    
        <xsl:for-each select=
        "key('kEventByMonthYear', string(current()/..))">
         <dd><xsl:value-of select="description"/></dd>
        </xsl:for-each>
     </xsl:template>
    </xsl:stylesheet>
    

    when applied on the following XML document:

    <events>
        <event>
            <date>
                <year>2012</year>
                <month>January</month>
            </date>
            <description>Jan1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>January</month>
            </date>
            <description>Jan2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>March</month>
            </date>
            <description>Mar1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>March</month>
            </date>
            <description>Mar2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>March</month>
            </date>
            <description>Mar3</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>July</month>
            </date>
            <description>Jul1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>July</month>
            </date>
            <description>Jul2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>September</month>
            </date>
            <description>Sep1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>October</month>
            </date>
            <description>Oct1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>October</month>
            </date>
            <description>Oct2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>November</month>
            </date>
            <description>Nov1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>November</month>
            </date>
            <description>Nov2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>December</month>
            </date>
            <description>Dec1</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>December</month>
            </date>
            <description>Dec2</description>
        </event>
        <event>
            <date>
                <year>2012</year>
                <month>December</month>
            </date>
            <description>Dec3</description>
        </event>
        <event>
            <date>
                <year>2013</year>
                <month>January</month>
            </date>
            <description>Jan1</description>
        </event>
        <event>
            <date>
                <year>2013</year>
                <month>January</month>
            </date>
            <description>Jan2</description>
        </event>
    </events>
    

    produces the wanted, correct result:

    <h2>2012</h2>
    <dl>
       <dt>January</dt>
       <dd>Jan1</dd>
       <dd>Jan2</dd>
       <dt>March</dt>
       <dd>Mar1</dd>
       <dd>Mar2</dd>
       <dd>Mar3</dd>
       <dt>July</dt>
       <dd>Jul1</dd>
       <dd>Jul2</dd>
       <dt>September</dt>
       <dd>Sep1</dd>
       <dt>October</dt>
       <dd>Oct1</dd>
       <dd>Oct2</dd>
       <dt>November</dt>
       <dd>Nov1</dd>
       <dd>Nov2</dd>
       <dt>December</dt>
       <dd>Dec1</dd>
       <dd>Dec2</dd>
       <dd>Dec3</dd>
    </dl>
    <h2>2013</h2>
    <dl>
       <dt>January</dt>
       <dd>Jan1</dd>
       <dd>Jan2</dd>
    </dl>
    

    Explanation:

    Proper use of the Muenchian method for grouping -- with composite grouping keys.