Setting and Using Variables and Parameters
by Bob DuCharme
February 07, 2001
A variable in XSLT has more in common with a variable in algebra
than with a variable in a typical programming language. It's a name
that represents a value and, within a particular application of a
template, it will never represent any other value -- it can't be reset
using anything described in the XSLT Recommendation. (Some XSLT
processors offer a special extension function to allow the resetting
of variables.)
XSLT variables actually have a lot more in common with constants in
many programming languages and are used for a similar purpose. If you
use the same value multiple times in your stylesheet, and there's a
possibility that you'll have to change them all to a different value,
it's better to assign that value to a variable and use references to
the variable instead. Then, if you need to change the value when
re-using the stylesheet, you only change the value assigned in the
creation of that variable.
For example, imagine that we want to turn this XML
<wine grape="Cabernet">
<winery>Duckpond</winery>
<product>Merit Selection</product>
<year>1996</year>
<price>11.99</price>
</wine>
into this HTML
<b><font size="10pt">Duckpond Cabernet</font></b><br>
<i><font size="10pt">Merit Selection</font></i><br>
<font size="10pt">1996</font><br>
<font size="10pt">11.99</font><br>
The following templates would accomplish this (all file names refer to files in this zip file),
<!-- xq340.xsl: converts xq338.xml into xq339.html -->
<xsl:template match="winery">
<b><font size="10pt"><xsl:apply-templates/>
<xsl:text> </xsl:text>
<xsl:value-of select="../@grape"/></font></b><br/>
</xsl:template>
<xsl:template match="product">
<i><font size="10pt"><xsl:apply-templates/></font></i><br/>
</xsl:template>
<xsl:template match="year | price">
<font size="10pt"><xsl:apply-templates/></font><br/>
</xsl:template>
but if you want to change the three font elements'
size attribute to "12pt", it would be too easy to miss one --
especially if the template rules weren't next to each other in the
stylesheet. The solution is to use a variable to represent this
size value:
<!-- xq341.xsl: converts xq338.xml into xq339.html -->
<xsl:variable name="bodyTextSize">10pt</xsl:variable>
<xsl:template match="winery">
<b><font size="{$bodyTextSize}"><xsl:apply-templates/>
<xsl:text> </xsl:text>
<xsl:value-of select="../@grape"/></font></b><br/>
</xsl:template>
<xsl:template match="product">
<i><font size="{$bodyTextSize}">
<xsl:apply-templates/></font></i><br/>
</xsl:template>
<xsl:template match="year | price">
<font size="{$bodyTextSize}"><xsl:apply-templates/></font><br/>
</xsl:template>
When referencing a variable or parameter from a literal result
element's attribute, you want the XSLT processor to plug in the
variable's value. You don't want a dollar sign followed by the
variable's name at that point in the template. To do this, put the
variable inside curly braces to make it an attribute value
template. To plug a variable's value into the content of a result tree
element, instead of an attribute value, use an xsl:value-of
instruction.
In the example above, if the $bodyTextSize variables were
not enclosed by curly braces, each font start-tag in the
result would have looked like this: <font
size="$bodyTextSize">.
The xsl:variable instruction creates a variable. Its
name attribute identifies the variable's name, and the value
can be specified either as the xsl:variable element's
contents (like the "10pt" in the example) or as the value of an
optional select attribute in the xsl:variable
element's start-tag.
The value of the select attribute must be an
expression. This offers two nice advantages:
-
It shows that the xsl:variable element isn't quite as
limited as the constants used by popular programming languages because
the variable's value doesn't need to be hardcoded when the stylesheet
is written.
-
The attribute value doesn't need curly braces to tell the XSLT
processor "this is an attribute value template, evaluate it as an
expression," because it always evaluates an xsl:variable
element's select attribute value as an
expression.
The two xsl:variable elements below have the same effect
as the one in the example above: they set the bodyTextSize
variable to a value of "10pt". The bodyTextSize variable has
its value assigned in a select attribute instead of in its
element content; the value assigned will be the return value of a
concat function that concatenates the string "pt" to the
result of adding $baseFontSize+2. What's
$baseFontSize? It's another variable, which is defined above
the bodyTextSize variable's xsl:variable
element. That value of "8" is added to 2 and concatenated to "pt" to
create a value of "10pt" for the bodyTextSize variable, which
can then be used just like the bodyTextSize variable in the
previous example.
<!-- xq342.xsl: converts xq338.xml into xq339.html -->
<xsl:variable name="baseFontSize" select="8"/>
<xsl:variable name="bodyTextSize"
select="concat($baseFontSize+2,'pt')"/>
The example above demonstrates some of the options available when
using an expression in the select attribute to assign a
variable's value. The second xsl:variable element references
another variable, does some math, and makes a function call. Variables
aren't as limited as many XSLT newcomers might think.
It also demonstrates another nice feature of variables: they don't
have to be strings. Once baseFontSize is set to "8", the
select value of the bodyTextSize variable's
xsl:variable element adds "2" to it and comes up with 10. If
the XSLT processor had treated these number as strings, putting "8"
and "2" together would get us "82". Instead, the XSLT processor treats
the baseFontSize variable as a number. It can treat a
variable as any type of object that can be returned by an XSLT
expression: a string, a number, a boolean value, or a node set. If an
XSLT variable has a value assigned by an xsl:variable
element's contents and by a select attribute, the XSLT
processor uses the one in the select attribute.
The examples above show "top-level" variables. They're defined with
xsl:variable elements that are children of the main
xsl:stylesheet element, making them global variables that can
be referenced anywhere in the stylesheet.
Variables can be "local" as well -- that is, defined inside of a
template rule and only available for use within that template
rule. For example, the following templates have the same result as the
ones in the examples above except that the font start-tag
before the result winery element's content has a value of
"12pt" in its size attribute instead of "10pt".
<!-- xq343.xsl: converts xq338.xml into xq344.html -->
<xsl:template match="wine">
<xsl:variable name="bodyTextSize">10pt</xsl:variable>
<xsl:apply-templates select="winery"/>
<i><font size="{$bodyTextSize}">
<xsl:apply-templates select="product"/>
</font></i><br/>
<font size="{$bodyTextSize}"><xsl:apply-templates select="year"/>
</font><br/>
<font size="{$bodyTextSize}"><xsl:apply-templates select="price"/>
</font><br/>
</xsl:template>
<xsl:template match="winery">
<xsl:variable name="bodyTextSize">12pt</xsl:variable>
<b><font size="{$bodyTextSize}"><xsl:apply-templates/>
<xsl:text> </xsl:text>
<xsl:value-of select="../@grape"/></font></b><br/>
</xsl:template>
The way these templates assign these size values is
different. Instead of one global bodyTextSize variable to use
throughout the stylesheet, the two template rules each have their own
bodyTextSize variables declared between their
xsl:template tags. The first one sets bodyTextSize
to a value of "10pt", and that's what gets plugged into the
size attribute values for the font tags that start
the product, year, and price elements. The
second template sets bodyTextSize to "12pt", so the
winery and grape element contents copied to the
result tree by that template start with font tags that have a
size value of "12pt":
<b><font size="12pt">Duckpond Cabernet</font></b><br>
<i><font size="10pt">Merit Selection</font></i><br>
<font size="10pt">1996</font><br>
<font size="10pt">11.99</font><br>
That's just a toy example. The next stylesheet uses a selection of
the string manipulation functions available in XSLT to right align the
result tree versions of the color elements in this
document.
<test>
<color>red</color>
<color>blue</color>
<color>yellow</color>
</test>
The fieldWidth global variable stores the desired column
width; the goal is to add spaces before each color value so
that the spaces plus the color name add up to this value.
<!-- xq346.xsl: converts xq345.xml into xq478.xml -->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output omit-xml-declaration="yes"/>
<xsl:variable name="fieldWidth">12</xsl:variable>
<xsl:template match="color">
<xsl:variable name="valueLength"
select="string-length(.)"/>
<xsl:variable name="padding"
select="$fieldWidth - $valueLength"/>
<xsl:value-of
select="substring(' ',1,$padding)"/>
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
The color element's template rule has two local
variables:
<
div class="itemizedlist">-
The value-length variable stores the length of the color
name using the string-length() function.
-
The padding variable stores the number of spaces required
to right-align the color name. It does this by subtracting the value
of the local valueLength variable from the global
fieldWidth variable.
Once the template rule knows how much space it needs to add to the
result tree before adding the color element's contents, it
adds that many spaces by using the substring() function to
pull that many spaces out of a string of spaces passed to the
substring() function as its first argument.
In the result, "red" has nine spaces before it, "blue" has eight,
and "yellow" has six:
red
blue
yellow
I could have done this without any local variables; in fact, when I
originally wrote this stylesheet, I did without them. As with any
programming language, using local variables made it easier to break
down the problem into pieces and to make the relationship of those
pieces easier to understand.
[1] [2] Next