You use <xsl:variable> to create variables in XSLT. This element has the following attributes:
name (mandatory). The name of the variable, set to a QName.
select (optional). An XPath expression that specifies the value of the variable. If you omit this attribute, the value of the variable is specified by the contents of <xsl:variable> .
This element can either be a top-level element or be used inside a template body. This element can contain a template body; but if a body is present, you should not use the select attribute.
You create a variable by assigning its name to the <xsl:variable> elements name attribute, and its value to the select attribute, as in this case, where Im creating a new variable named number_books and storing the value 255 in it:
<xsl:variable name="number_books" select="255"/> . . .
You can refer to the value in this variable by prefixing the variables name with a $ like this:
<xsl:variable name="number_books" select="255"/> <xsl:text>There are </xsl:text> <xsl:value-of select="$number_books"/> <xsl:text> books in my library </xsl:text>
Note that if you assign a literal to a variable, as in assigning the value turkey to the variable sandwich , you must enclose the literal in quotes (which must be different from the quotes youre using to enclose attribute values):
<xsl:variable name="sandwich" select="'turkey'"/>
In XSLT 1.0, you didnt have to use the select attributeyou could enclose data inside the <xsl:variable> itself this way:
<xsl:variable name="sandwich">turkey</xsl:variable>
Technically, however, when you omit the select attribute from <xsl:variable> or <xsl: with-param > and give those elements some contents, you are creating a result tree fragment , which is no longer legal in XSLT 1.1.
Its also worth noting that the name of the variable includes its prefix, if any, such as star:PLANET . If there is a prefix, it must correspond to an active namespace. Comparisons are made not by comparing prefixes but by checking the actual URI of the prefix, so star:PLANET could be the same as nebula:PLANET if the namspaces star and nebula correspond to the same URI.
You can use <xsl:variable> as a top-level element or inside a template body to create variables. Variables created in top-level <xsl:variable> elements have global scope , whereas those created in template bodies have local scope . The scope of a variable indicates in what part of the stylesheet you can use it.
The scope of a global variable is the entire stylesheet, including imported or included stylesheets. That means its available throughout the whole stylesheet, unless it is overridden by a local variable with the same name. You can even refer to a global variable before its declared. However, you cannot have circular references. For example, if you declare a in terms of b , you cannot declare b in terms of a .
The scope of a local variable is limited to its following siblings, or descendants of following siblings. In particular, this means that if you declare a variable inside an element such as <xsl:choose> , <xsl:if> , or <xsl:for-each> , it wont be available outside that element.
You cannot usually change a variables value, but you can override it with a more local version. That is, local variables can override global ones while the local variables are in scope. For example, say that I declare a variable named movie :
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> . . .
This is a top-level element, so movie is global. Even inside templates, movie still has its original value, if there is no local variable with the same name:
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:template match="entertainment"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> . . .
However, if you declare a local variable named movie , that new version overrides the global version in the template:
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:template match="entertainment"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:variable name="movie" select="'Goldfinger'"> <!-- $movie = 'Goldfinger' here--> . . .
In this case, weve overridden the global variable with a local one. But note that you cant declare the same variable again in the same template in an attempt to change its value:
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:template match="entertainment"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:variable name="movie" select="'Goldfinger'"> <!-- $movie = 'Goldfinger' here--> <xsl:variable name="movie" select="'Withnail and I'"><!-- Not allowed --> . . .
Outside the template, the local variable is invisible, and movie holds the global value:
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:template match="entertainment"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:variable name="movie" select="'Goldfinger'"> <!-- $movie = 'Goldfinger' here--> <xsl:variable name="movie" select="'Withnail and I'"><!-- Not allowed --> </xsl:template> <!-- $movie = 'Mr. Blandings Builds His Dream House' here---> . . .
Note that you cant redeclare a global variable either:
<xsl:variable name="movie" select="'Mr. Blandings Builds His Dream House'"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:template match="entertainment"> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:variable name="movie" select="'Goldfinger'"> <!-- $movie = 'Goldfinger' here--> <xsl:variable name="movie" select="'Withnail and I'"><!-- Not allowed --> </xsl:template> <!-- $movie = 'Mr. Blandings Builds His Dream House' here--> <xsl:variable name="movie" select="'Goldfinger'"><!-- Not allowed -->
Despite all these restrictions, you can reset a variables value each time through an <xsl:for-each> loop, as youll see in the next section.
Lets take a look at some examples that use variables. In this example, I assign a copyright notice to a variable, copyright , and then use that variable to add a copyright attribute to each element in planets.xml:
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:variable name="copyright" select="'(c)2002 Starpowder Inc.'"/> <xsl:template match="*"> <xsl:copy> <xsl:attribute name="copyright"> <xsl:value-of select="$copyright"/> </xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Heres the result document, complete with copyright attributes:
<?xml version="1.0" encoding="utf-8"?> <PLANETS copyright="(c)2002 Starpowder Inc."> <PLANET copyright="(c)2002 Starpowder Inc."> <NAME copyright="(c)2002 Starpowder Inc.">Mercury</NAME> <MASS copyright="(c)2002 Starpowder Inc.">.0553</MASS> <DAY copyright="(c)2002 Starpowder Inc.">58.65</DAY> <RADIUS copyright="(c)2002 Starpowder Inc.">1516</RADIUS> <DENSITY copyright="(c)2002 Starpowder Inc.">.983</DENSITY> <DISTANCE copyright="(c)2002 Starpowder Inc.">43.4</DISTANCE> </PLANET> <PLANET copyright="(c)2002 Starpowder Inc."> <NAME copyright="(c)2002 Starpowder Inc.">Venus</NAME> <MASS copyright="(c)2002 Starpowder Inc.">.815</MASS> <DAY copyright="(c)2002 Starpowder Inc.">116.75</DAY> <RADIUS copyright="(c)2002 Starpowder Inc.">3716</RADIUS> <DENSITY copyright="(c)2002 Starpowder Inc.">.943</DENSITY> <DISTANCE copyright="(c)2002 Starpowder Inc.">66.8</DISTANCE> </PLANET> . . .
Heres another example. Variables are often useful for storing context-sensitive values, and Ill do so in an example I mentioned at the beginning of this chapter. In this case, Ill convert planets.xml to a new document with one element for each planet. Each of these new elements contains two <SIBLINGPLANET> elements, giving the sibling planets of the current planet for example, Earths siblings are Venus and Mercury:
<?xml version="1.0" encoding="utf-8"?> <Mercury> <SIBLINGPLANET> Venus </SIBLINGPLANET> <SIBLINGPLANET> Earth </SIBLINGPLANET> </Mercury> <Venus> <SIBLINGPLANET> Mercury </SIBLINGPLANET> <SIBLINGPLANET> Earth </SIBLINGPLANET> </Venus> <Earth> <SIBLINGPLANET> Mercury </SIBLINGPLANET> <SIBLINGPLANET> Venus </SIBLINGPLANET> </Earth>
To implement this example, Ill match each <PLANET> element in turn , and use an <xsl:for-each> loop to loop over all planets, creating <SIBLINGPLANET> elements for all planets that are not the context node. However, when Im inside the <xsl:for-each> element, how do I know which planet is the context node that the template matched? Inside the <xsl:for-each> element, . refers to the current node on which <xsl:for-each> is working, not the templates context node. To solve this problem, I can store the context node in a variable, which I name contextnode :
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <xsl:for-each select="PLANET"> <xsl:element name="{NAME}"> <xsl:variable name="contextnode" select="."/> . . .
Now I can refer to the templates context node as $contextnode in the <xsl:for-each> element when Im checking to make sure the current element in the <xsl:for-each> loop isnt the context node:
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="PLANETS"> <xsl:for-each select="PLANET"> <xsl:element name="{NAME}"> <xsl:variable name="contextnode" select="."/> <xsl:for-each select="//PLANET"> <xsl:if test=". != $contextnode"> <xsl:element name="SIBLINGPLANET"> <xsl:value-of select="NAME"/> </xsl:element> </xsl:if> </xsl:for-each> </xsl:element> </xsl:for-each> </xsl:template> </xsl:stylesheet>
And thats all it takes.
When the <xsl:variable> has a body, it creates a variable whose value is a result tree fragment. In the following example I use a result tree fragment to supply a default value for an attribute named COLOR if that attribute doesnt have a value already. In this case, Im setting the default value to blue:
<xsl:variable name="COLOR"> <xsl:choose> <xsl:when test="@COLOR"> <xsl:value-of select="@COLOR"/> </xsl:when> <xsl:otherwise>blue</xsl:otherwise> </xsl:choose> </xsl:variable>
The string value of the result tree fragment (that is, either the value of the COLOR attribute or the default value, blue) is assigned to the variable $COLOR . Now you can refer to the value of the variable, $COLOR , in XPath expressions rather than the attributes value, @COLOR , and be confident that youll always be working with a real color, even if the corresponding element doesnt have a COLOR attribute.
The following is another result tree fragment example; in this case, I store a literal result element in a variable, START_HTML :
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:variable name="START_HTML"> <HEAD> <TITLE> My page </TITLE> </HEAD> </xsl:variable> . . .
Now I can use this literal result element where ever I want:
<?xml version="1.0"?> <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html"/> <xsl:variable name="START_HTML"> <HEAD> <TITLE> My page </TITLE> </HEAD> </xsl:variable> <xsl:template match="PLANETS"> <HTML> <xsl:copy-of select="$START_HTML"/> <BODY> <H1>Welcome to my page</H1> </BODY> </HTML> </xsl:template> </xsl:stylesheet>
And heres the result:
<HTML> <HEAD> <TITLE> My page </TITLE> </HEAD> <BODY> <H1>Welcome to my page</H1> </BODY> </HTML>
However, now that result tree fragments are illegal in XSLT 1.1, this wont work anymore. So how do you store an entire literal result element in one easy-to-call package? You can create a named template .