Book HomeJava and XSLTSearch this book

2.5. Looping and Sorting

As shown throughout this chapter, you can use <xsl:apply-templates ...> to search for patterns in an XML document. This type of processing is sometimes referred to as a " data driven" approach because the data of the XML file drives the selection process. Another style of XSLT programming is called "template driven," which means that the template's code tends to drive the selection process.

2.5.1. Looping with <xsl:for-each>

Sometimes it is convenient to explicitly drive the selection process with an <xsl:for-each> element, which is reminiscent of traditional programming techniques. In this approach, you explicitly loop over a collection of nodes without instantiating a separate template as <xsl:apply-templates> does. The syntax for <xsl:for-each> is as follows:

<xsl:for-each select="president">
  ...content for each president element
</xsl:for-each>

The select attribute can contain any XPath location path, and the loop will iterate over each element in the resulting node set. In this example, the context is <president> for all content within the loop. Nested loops are possible and could be used to loop over the list of <vicePresident> elements.

2.5.2. Sorting

Sorting can be applied in either a data-driven or template-driven approach. In either case, <xsl:sort> is added as a child element to something else. By adding several consecutive <xsl:sort> elements, you can accomplish multifield sorting. Each sort can be in ascending or descending order, and the data type for sorting is either "number" or "text". The sort order defaults to ascending. Some examples of <xsl:sort> include:

<xsl:sort select="first"/>
<xsl:sort select="last" order="descending"/>
<xsl:sort select="term/@from" order="descending" data-type="number"/>
<xsl:sort select="name/first" data-type="text" case-order="upper-first"/>

In the last line, the case-order attribute specifies that uppercase letters should be alphabetized before lowercase letters. The other accepted value for this attribute is lower-first. According to the specification, the default behavior is "language dependent."

2.5.3. Looping and Sorting Examples

The easiest way to learn about looping and sorting is to play around with a lot of small examples. The code in Example 2-9 applies numerous different looping and sorting strategies to our list of presidents. Comments in the code indicate what is happening at each step.

Example 2-9. Looping and sorting

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <html>
      <body>
        <h1>Sorting Examples</h1>
        <xsl:apply-templates select="presidents"/>
      </body>
    </html>
  </xsl:template>
  <!--********************************************************************
      ** presidents template
      *****************************************************************-->
  <xsl:template match="presidents">
    <!--*****************************************************************
        ** Sorting using xsl:for-each
        **************************************************************-->
    <h2>All presidents sorted by first name using xsl:for-each</h2>
    <xsl:for-each select="president">
      <xsl:sort select="name/first"/>
      <xsl:apply-templates select="name"/>
    </xsl:for-each>
    <!--*****************************************************************
        ** Sorting using xsl:apply-templates
        **************************************************************-->
    <h2>All presidents sorted by first name using xsl:apply-templates</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="first"/>
    </xsl:apply-templates>
    <h2>All presidents sorted by date using xsl:apply-templates</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="../term/@from" data-type="number" order="descending"/>
    </xsl:apply-templates>
    <!--*****************************************************************
        ** Multi-field sorting
        **************************************************************-->
    <h2>Multi-field sorting example</h2>
    <xsl:apply-templates select="president/name">
      <xsl:sort select="last"/>
      <xsl:sort select="first" order="descending"/>
    </xsl:apply-templates>
    <!--*****************************************************************
        ** Nested xsl:for-each loops
        **************************************************************-->
    <h2>All presidents and vice presidents using xsl:for-each</h2>
    <ul>
      <xsl:for-each select="president">
        <xsl:sort select="name/first" order="descending"/>
        <li>
          <xsl:apply-templates select="name"/>
        </li>
        <ul>
          <xsl:for-each select="vicePresident">
            <xsl:sort select="name/first"/>
            <li>
              <xsl:apply-templates select="name"/>
            </li>
          </xsl:for-each>
        </ul>
      </xsl:for-each>
    </ul>
    <!--*****************************************************************
        ** Same as previous, only using xsl:apply-templates
        **************************************************************-->
    <h2>All presidents and vice presidents using xsl:apply-templates</h2>
    <ul>
      <xsl:apply-templates select="president">
        <xsl:sort select="name/first" order="descending"/>
      </xsl:apply-templates>
    </ul>
  </xsl:template>
  <!--*****************************************************************
      ** 'president' template, outputs the president's name and vice
      **             president's name.
      **************************************************************-->
  <xsl:template match="president">
    <li>
      <xsl:apply-templates select="name"/>
    </li>
    <ul>
      <xsl:for-each select="vicePresident">
        <xsl:sort select="name/first"/>
        <li>
          <xsl:apply-templates select="name"/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:template>
  <!--*****************************************************************
      ** name template, outputs first, middle, and last name
      **************************************************************-->
  <xsl:template match="name">
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="first"/>
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="middle"/>
    <xsl:text disable-output-escaping="yes">&amp;nbsp;</xsl:text>
    <xsl:value-of select="last"/>
    <br/>
  </xsl:template>
</xsl:stylesheet>

Notice that when applying a sort to <xsl:apply-templates>, that element can no longer be an empty element. Instead, one or more <xsl:sort> elements are added as children of <xsl:apply-templates>. You should also note that sorting cannot occur in the <xsl:template match="name"> element. The reason for this is simple: at the <xsl:apply-templates> end, you have a list of nodes to sort. By the time the processing reaches <xsl:template match="name">, the search has narrowed down to a single <name>, so there is no node list left to sort.



Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.