home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book HomeXSLSearch this book

4.2. Branching Elements of XSLT

Three XSLT elements are used for branching: <xsl:if>, <xsl:choose>, and <xsl:for-each>. The first two are much like the if and case statements you may be familiar with from other languages, while the for-each element is significantly different from the for or do-while structures in other languages. We'll discuss all of them here.

4.2.1. The <xsl:if> Element

The <xsl:if> element looks like this:

<xsl:if test="count(zone) &gt; 2">
  <xsl:text>Applicable zones: </xsl:text>
  <xsl:apply-templates select="zone"/>
</xsl:if>

The <xsl:if> element, surprisingly enough, implements an if statement. The element has only one attribute, test. If the value of test evaluates to the boolean value true, then all elements inside the <xsl:if> are processed. If test evaluates to false, then the contents of the <xsl:if> element are ignored. (If you want to implement an if-then-else statement, check out the <xsl:choose> element described in the next section.)

Notice that we used &gt; instead of > in the attribute value. You're always safe using &gt; here, although some XSLT processors process the greater-than sign correctly if you use > instead. If you need to use the less-than operator (<), you'll have to use the &lt; entity. The same holds true for the less-than-or-equal operator (<=) and the greater-than-or-equal (>=) operators. See Section B.4.2, "Boolean Operators" for more information on this topic.

4.2.1.2. Boolean examples

Here are some examples that illustrate how boolean values evaluate the test attribute:

<xsl:if test="count(zone) &gt;= 2">
This is a boolean expression because it uses the greater-than-or-equal boolean operator. If the count() function returns a value greater than or equal to 2, the test attribute is true. Otherwise, the test attribute is false.

<xsl:if test="$x">
The variable x is evaluated. If it is a string, then the test attribute is true only if the string has a length greater than zero. If it is a node-set, then the test attribute is true only if the node-set has at least one member. If it is a number, then the test attribute is true only if the number is anything other than positive zero, negative zero, or NaN. (Of course, if x is a boolean value, true is true and false is false.)

<xsl:if test="true()">
The boolean function true() always returns the boolean value true. Therefore, this test attribute is always true.

<xsl:if test="true">
This example is a trick. This test attribute is true only if there is at least one <true> element in the current context. The XSLT processor interprets the value true as an XPath expression that specifies all <true> elements in the current context. The strings true and false don't have any special significance in XSLT.

<xsl:if test="'true'">
This test attribute is always true. Notice that in this case we used single quotes inside double quotes to specify that this is a literal string, not an element name. This test attribute is always true because the string has a length greater than zero, not because its value happens to be the word "true."

<xsl:if test="'false'">
Another trick example; this test attribute is always true. As before, we used single quotes inside double quotes to specify that this is a literal string. Because the string has a length greater than zero, the test attribute is always true. The value of the nonempty string, confusing as it is, doesn't matter.

<xsl:if test="not(3)">
This test attribute is always false. The literal 3 evaluates to true, so its negation is false. On the other hand, the expressions not(0) and not(-0) are always true.

<xsl:if test="false()">
This test attribute is always false. The boolean function false() always returns the boolean value false.

<xsl:if test="section/section">
The XPath expression section/section returns a node-set. If the current context contains one or more <section> elements that contain a <section> element in turn, the test attribute is true. If no such elements exist in the current context, the test attribute is false.

4.2.2. The <xsl:choose> Element

The <xsl:choose> element is the equivalent of a case or switch statement in other programming languages. You can also use it to implement an if-then-else statement. An <xsl:choose> contains at least one <xsl:when> element (logically equivalent to an <xsl:if> element), with an optional <xsl:otherwise> element. The test attribute of each <xsl:when> element is evaluated until the XSLT processor finds one that evaluates to true. When that happens, the contents of that <xsl:when> element are evaluated. If none of the <xsl:when> elements have a test that is true, the contents of the <xsl:otherwise> element (if there is one) are processed.

Here's how these XSLT elements compare to the switch or select/case statements you might know from other languages:

  • The C, C++, and Java switch statement is roughly equivalent to the <xsl:choose> element. The one exception is that procedural languages tend to use fallthrough processing. In other words, if a branch of the switch statement evaluates to true, the runtime executes everything until it encounters a break statement, even if some of that code is part of other branches. The <xsl:choose> element doesn't work that way. If a given <xsl:when> evaluates to true, only the statements inside that <xsl:when> are evaluated.

  • The Java case statement is equivalent to the <xsl:when> element. In Java, if a given case statement does not end with a break statement, the following case is executed as well. Again, this is not the case with XSLT; only the contents of the first <xsl:when> element that is true are processed.

  • The Java and C++ default statement is equivalent to the <xsl:otherwise> element.

4.2.2.1. <xsl:choose> example

Here's a sample <xsl:choose> element that sets the background color of the table's rows. If the bgcolor attribute is coded on the <table-row> element, the value of that attribute is used as the color; otherwise, the sample uses the position() function and the mod operator to cycle the colors between papayawhip, mintcream, lavender, and whitesmoke.

<xsl:template match="table-row">
  <tr>
    <xsl:attribute name="bgcolor">
     <xsl:choose>
        <xsl:when test="@bgcolor">
          <xsl:value-of select="@bgcolor"/>
        </xsl:when>
        <xsl:when test="position() mod 4 = 0">
          <xsl:text>papayawhip</xsl:text>
        </xsl:when>
        <xsl:when test="position() mod 4 = 1">
          <xsl:text>mintcream</xsl:text>
        </xsl:when>
        <xsl:when test="position() mod 4 = 2">
          <xsl:text>lavender</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>whitesmoke</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
    <xsl:apply-templates select="*"/>
  </tr>
</xsl:template>

In this sample, we use <xsl:choose> to generate the value of the bgcolor attribute of the <tr> element. Our first test is to see if the bgcolor attribute of the <table-row> element exists; if it does, we use that value for the background color and the <xsl:otherwise> and other <xsl:when> elements are ignored. (If the bgcolor attribute is coded, the XPath expression @bgcolor returns a node-set containing a single attribute node.)

The next three <xsl:when> elements check the position of the current <table-row> element. The use of the mod operator here is the most efficient way to cycle between the various options. Finally, we use an <xsl:otherwise> element to specify whitesmoke as the default case. If position() mod 4 = 3, the background color will be whitesmoke.

A couple of minor details: in this example, we could replace the <xsl:otherwise> element with <xsl:when test="position() mod 4 = 3">; that is logically equivalent to the example as coded previously. For obfuscation bonus points, we could code the second <xsl:when> element as <xsl:when test="not(position() mod 4)">. (Remember that the boolean negation of zero is true.)

4.2.3. The <xsl:for-each> Element

If you want to process all the nodes that match a certain criteria, you can use the <xsl:for-each> element. Be aware that this isn't a traditional for loop; you can't ask the XSLT processor to do something like this:

for i = 1 to 10 do

The <xsl:for-each> element lets you select a set of nodes, then do something with each of them. Let me mention again that this is not the same as a traditional for loop. Another important point is that the current node changes with each iteration through the <xsl:for-each> element. We'll go through some examples to illustrate this.

4.2.3.1. <xsl:for-each> example

Here's a sample that selects all <section> elements inside a <tutorial> element and then uses a second <xsl:for-each> element to select all the <panel> elements inside each <section> element:

<xsl:template match="tutorial">
  <xsl:for-each select="section">
    <h1>
      <xsl:text>Section </xsl:text>
      <xsl:value-of select="position()"/>
      <xsl:text>. </xsl:text>
      <xsl:value-of select="title"/>
    </h1>
    <ul>
      <xsl:for-each select="panel">
        <li>
          <xsl:value-of select="position()"/>
          <xsl:text>. </xsl:text>
          <xsl:value-of select="title"/>
        </li>
      </xsl:for-each>
    </ul>
  </xsl:for-each>
</xsl:template>

Given this XML document:

<tutorial>
  <section>
    <title>Gene Splicing for Young People</title>
    <panel>
      <title>Introduction</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>Discovering the secrets of life and creation</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>"I created him for good, but he's turned out evil!"</title>
      <!-- ... -->
    </panel>
    <panel>
      <title>When angry mobs storm your castle</title>
      <!-- ... -->
    </panel>
  </section>
</tutorial>

The previous template produces these results:

<h1>Section 1. Gene Splicing for Young People</h1>
<ul>
  <li>1. Introduction</li>
  <li>2. Discovering the secrets of life and creation</li>
  <li>3. "I created him for good, but he's turned out evil!"</li>
  <li>4. When angry mobs storm your castle</li>
</ul>

Each time a select attribute is processed, it is evaluated in terms of the current node. As the XSLT processor cycles through all the <xsl:section> and <xsl:panel> elements, each of them in turn becomes the current node. By using iteration, we've generated a table of contents with a very simple template.



Library Navigation Links

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