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.
The <xsl:if>
element looks like this:
<xsl:if test="count(zone) > 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 >
instead of >
in the attribute value. You’re always safe using >
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 <
entity. The same holds true for the less-than-or-equal operator (<=
) and the greater-than-or-equal (>=
) operators. See Section B.4.2 for more information on this topic.
The <xsl:if>
element is pretty simple, but it’s the first time we’ve had to deal with boolean values. These values will come up later, so we might as well discuss them here. Attributes like the test
attribute of the <xsl:if>
element convert whatever their values happen to be into a boolean value. If that boolean value is true
, the <xsl:if>
element is processed. (The <xsl:when>
element, which we’ll discuss in just a minute, has a test
attribute as well.)
Here’s the rundown of how various datatypes are converted to boolean values:
- number
If a number is positive or negative zero, it is
false
. If a numeric value isNaN
(not a number; if I try to use the string “blue” as a number, the result isNaN
), it isfalse
. If a number has any other value, it istrue
.- node-set
- string
A zero-length string is
false
; a string whose length is not zero istrue
.
These rules are defined in Section 4.3 of the XPath specification.
Here are some examples that illustrate how boolean values evaluate the test
attribute:
-
<xsl:if test="count(zone) >= 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, thetest
attribute istrue
. Otherwise, thetest
attribute isfalse
.-
<xsl:if test="$x">
The variable
x
is evaluated. If it is a string, then thetest
attribute istrue
only if the string has a length greater than zero. If it is a node-set, then thetest
attribute istrue
only if the node-set has at least one member. If it is a number, then thetest
attribute istrue
only if the number is anything other than positive zero, negative zero, orNaN
. (Of course, ifx
is a boolean value,true
istrue
andfalse
isfalse
.)-
<xsl:if test="true()">
The boolean function
true()
always returns the boolean valuetrue
. Therefore, thistest
attribute is alwaystrue
.-
<xsl:if test="true">
This example is a trick. This
test
attribute istrue
only if there is at least one<true>
element in the current context. The XSLT processor interprets the valuetrue
as an XPath expression that specifies all<true>
elements in the current context. The stringstrue
andfalse
don’t have any special significance in XSLT.-
<xsl:if test="'true'">
This
test
attribute is alwaystrue
. Notice that in this case we used single quotes inside double quotes to specify that this is a literal string, not an element name. Thistest
attribute is alwaystrue
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 alwaystrue
. 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, thetest
attribute is alwaystrue
. The value of the nonempty string, confusing as it is, doesn’t matter.-
<xsl:if test="not(3)">
This
test
attribute is alwaysfalse
. The literal3
evaluates totrue
, so its negation isfalse
. On the other hand, the expressionsnot(0)
andnot(-0)
are alwaystrue
.-
<xsl:if test="false()">
This
test
attribute is alwaysfalse
. The boolean functionfalse()
always returns the boolean valuefalse
.-
<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, thetest
attribute istrue
. If no such elements exist in the current context, thetest
attribute isfalse
.
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 givencase
statement does not end with abreak
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 istrue
are processed.The Java and C++
default
statement is equivalent to the<xsl:otherwise>
element.
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
.)
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.
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.
Get XSLT now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.