The xsl:variable Element: Creating Variables

The <xsl:variable> Element: Creating Variables

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.

Variable Scope

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.

Variables at Work

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:

Listing 9.1 Using a Variable
 <?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:

Listing 9.2 Using a Variable to Store Context-Sensitive Information
 <?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 .



Inside XSLT
Inside Xslt
ISBN: B0031W8M4K
EAN: N/A
Year: 2005
Pages: 196

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net