Chapter 8

Variables in XSLT: A Breed Apart

In This Chapter

bullet Declaring variables

bullet Setting variables in templates

bullet Using variables to save time

bullet Calculating values with variables

bullet Determining variable scope

D o you like canned spinach? Unless you have a name that begins with a P, forearms the size of thighs, and a girlfriend named Olive Oyl, I suspect you’d much prefer a fresh spinach salad instead. Okay, maybe spinach is pushing it, but I bet you’d at least go for the fresh part, right? Prepackaged goods are helpful when you need to prepare food in advance of their actual use, but people don’t much like the notion of “canned” anything if they can help it.

In earlier chapters of this book, I walk you through a lot of XSLT. All the information plugged into result documents until this chapter has come from XML or from “canned” text. That works fine in the earlier chapters of this book, but just as one longs for fresh veggies after a weekend camping trip of SPAM and beef jerky, here I show you how to gather and utilize “fresh” data through the use of variables and parameters.

What Is a Variable?

A variable is a handy little gadget used in nearly every programming language. Not to be left out, XSLT has variables as well, but they’re quite different from those you find in Visual Basic, JavaScript, C++, or Java. They are truly their own breed.

In XSLT, a variable is used to represent something — perhaps a chunk of text, a numeric value, boilerplate HyperText Markup Language (HTML), or even a collection of XML tree nodes. But, in a manner quite different from its cousins in other languages, an XSLT variable can’t change its value after you declare it. Actually, if you want to draw any comparisons, an XSLT variable is perhaps closest to a constant in a traditional programming language.

XSLT is quite powerful by using template rules and XPath expressions to transform XML into pretty much any output that you can imagine. Variables help to augment these tools by enabling you to make what techies call run-time changes (or for the rest of us, changes that happen at the time the XSLT is processed).

Run-time changes enable your transformation to adapt to a condition that exists at the time of execution rather than having all the “canned” answers prepared beforehand. Admittedly, XSLT variables are limited in their ability to do many things on the fly because you can’t modify them after they’re defined.

Setting Variables

A variable is defined in XSLT in one of two ways: either as a content variable or a select attribute variable.

Content variables

The most straightforward way to set a variable is by using the xsl:variable element to assign a variable name to the content enclosed by its tags.

<xsl:variable name=”mystate”>Massachusetts</xsl:variable> 

In the preceding example, the mystate variable represents the string value of Massachusetts. Variables come in handy when you don’t want to type out long pieces of text over and over again in your stylesheet.

In order to use a content variable, you first need to let the XSLT processor know your intentions. You can’t just enter the variable’s name inside the stylesheet as is and expect it to be treated properly. XSLT processors are smart, but they don’t know whether you want it to be handled as normal text or as a variable identifier. If you don’t tell the processor that a variable is on its way, it opts for the easy road and treats it as ordinary text. Thus, to tell the processor that you want to use mystate as a variable, prefix it with a $ character.

$mystate 

This fully prepared variable then needs to be plugged into your stylesheet in the appropriate context. Just exactly where you place it in your stylesheet depends on how you want the variable to be placed in the result document.

If you want to output the variable as text, use the xsl:value-of element,

<p>I love <xsl:value-of select=”$mystate”/> in the Fall.</p>

which is spit out in HTML as

<p>I love Massachusetts in the Fall.</p>

(See Chapter 4 for more information on using the xsl:value-of instruction.)

Or, to insert the variable into your XML output as an attribute, use an attribute value template,

<location state=”{$mystate}”/>

which results in

<location state=”Massachusetts”/>

(See Chapter 5 for more information on attribute value templates.)

The value of a content variable is always the piece of text enclosed by the xsl:variable element. Even a number placed inside is converted to a string during transformation.

If you’ve been around computers much, you’ve probably heard the term WYSIWYG (What You See Is What You Get) — what you see on-screen looks the same as when it’s printed. Content variables could similarly be described as WYSIWYG because what you see in the XSLT markup that you create is exactly what appears in the result document.

Select attribute variables

A second way to define a variable is to use the xsl:variable element with a select attribute added to it.

<xsl:variable name=”mystate” select=”’Massachusetts’”/>

This kind of variable is sometimes referred to as a select attribute variable. Whereas a content variable sees all values as text, a select attribute variable treats its value as the result of an expression. An expression is an XSLT statement that represents a value or is used to calculate a value. See Table 8-1 for some sample XSLT expressions and their results.

Table 8-1 Results of XSLT Expressions
Expression Result
8-5 3
‘Text’ Text
round(5.129) 5

Content variables are WYSIWYG, but select attribute variables often are not. As you can see in Table 8-1, the result of an expression typically looks different than the expression itself.

In the code sample at the beginning of this section, notice that I add single quotation marks around Massachusetts when placing it inside of a select attribute. If I don’t add single quotation marks, XSLT tries to evaluate Massachusetts as an expression. Admittedly, I’ve certainly heard my share of expressions in Massachusetts — particularly from other drivers when I’m driving around downtown Boston — but that’s not what I had in mind here. If I don’t add the quotes, I get a goose egg from the XSLT processor when I try to plug in that variable into the stylesheet. But by including the quotes, I let the processor know that it should treat Massachusetts as a literal string.

Although you can use a select attribute variable anywhere that you would use a content variable for outputting text, its primary use is working with number values or node sets.

Remember these three final “gotchas” when you define variables:

bullet If you have a select attribute defined for an xsl:variable, you can’t define any content (text between the start and end tags) or you get a processing error.

bullet Conversely, if the variable doesn’t have any content defined and doesn’t have a select attribute, its result is an empty string.

bullet A content variable needs to have a start and an end tag (<xslt: variable name=”x”>y</xsl:variable>), whereas a select attribute variable should use an empty tag (<xslt:variable name=”x” select=”’y’”/>) because it contains no content.

Uses of Variables

Variables play a less integral role in standard XSL transformations than their equivalents in C++ or Visual Basic do. But they remain useful devices — in particular, when you want to reuse values across your stylesheet and when you want to calculate values once and use the result in your output document.

Using a variable as a shortcut

Perhaps the most common purpose for XSLT variables is simply to use them as shortcuts to values that you intend to use multiple times across a document. For example, suppose that you have a standard footer that you want to place at the bottom of a result document that you produce. Save time and effort by defining a variable once, having it reference a chunk of HTML.

<xsl:variable name=”footer”>

<p><font size=”8pt”>Copyright (c)2001, Variably Speaking, Inc.</font></p>

<p><font size=”8pt”>Send us email at <a href=”mailto:[email protected]”>[email protected]</a></font>

</p>

</xsl:variable>

To use this footer variable, I reference it using the xsl:value-of element,

<xsl:value-of select=”$footer”/>

which outputs the following HTML in the result document.

<p><font size=”8pt”>Copyright (c)2001, Variably Speaking, Inc.</font></p>

<p><font size=”8pt”>Send us email at <a     href=”mailto:[email protected]”>[email protected]</a>

<font></p>

These shortcut variables are very easy to define and use in your XSLT stylesheets. They come in especially handy when you need to use hard-to-remember constants such as HTML color-code values or special characters.

Variables also come in handy when you need to reuse a value within the same stylesheet. For example, suppose that I want to transform the following XML snippet:

  <film name=”Henry V”>

     <director>Kenneth Branagh</director>

     <writer>Kenneth Branagh, William Shakespeare</writer>

     <year>1989</year>

     <runtime>137</runtime> 

     <sound>Stereo</sound>

     <genre>Drama</genre>

     <score>10.0</score>

     <mpaa>PG-13</mpaa>

  </film>

and output it into this HTML:

<p>Highest movies by score:</p>

<b>

<font color=”FF0000” size=”2”>Henry V by Kenneth Branagh</font>

</b>

<br/>

<i>

<font color=”FF0000” size=”-2”>Received a score of: 10.0</font>

</i>

<br/>

Looking at the resulting HTML, notice that the font color is used twice. And, although the two specified font sizes are different, they’re relative to each other. Therefore, in my XSLT stylesheet, I can use variables to represent both the font color and size.

<xsl:variable name=”myfontcolor”>FF0000</xsl:variable> 

<xsl:variable name=”myfontsize” select=”2”/>

To get the results for the first output section, I create a template rule for the director element, plugging in the myfontcolor and myfontsize variables.

<xsl:template match=”director”>

   <p>Highest movies by score:</p>

   <b><font color=”{$myfontcolor}” size=”{$myfontsize}”>

   <xsl:value-of select=”../@name”/>   

   <xsl:text> by </xsl:text>

   <xsl:apply-templates/>

   </font></b><br/>

</xsl:template>

For the score template rule, the myfontcolor variable is simply placed as is. However, because I want the font size of this text to be four increments smaller than the myfontsize value, I subtract one from the variable. The XSLT processor evaluates {$myfontsize - 4} as an XPath expression and uses the result of the expression (-2) in the output document.

<xsl:template match=”score”>

   <i><font size=”{$myfontsize-4}”> 

   Received a score of:  

   <xsl:apply-templates/>

   </font></i><br/>       

</xsl:template>

The complete XSL stylesheet is shown below.

<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” version=”1.0”> 

<!-- Define variables -->

<xsl:variable name=”myfontcolor”>FF0000</xsl:variable> 

<xsl:variable name=”myfontsize” select=”2”/>

<!-- Director template  -->

<xsl:template match=”director”>

   <p>Highest movies by score:</p>

   <b><font size=”{$myfontsize}” color=”{$myfontcolor}”>

   <xsl:value-of select=”../@name”/>   

   <xsl:text> by </xsl:text>

   <xsl:apply-templates/>

   </font></b><br/>

</xsl:template>

<!-- Score template  -->

<xsl:template match=”score”>

   <i><font size=”{$myfontsize - 4}”> 

   Received a score of:  

   <xsl:apply-templates/>

   </font></i><br/>       

</xsl:template>

<!-- Remove these elements from our results document --> 

<xsl:template match=”year”/>

<xsl:template match=”writer”/>

<xsl:template match=”sound”/>

<xsl:template match=”genre”/>

<xsl:template match=”mpaa”/>

<xsl:template match=”runtime”/>

</xsl:stylesheet> 

See the results on the transformation in Internet Explorer in Figure 8-1.

Figure 8-1: Results of the XSLT transformation.

Figure 8-1: Results of the XSLT transforma-tion.

Using a variable as a calculator

Because select attribute variables return expressions rather than ordinary text, they’re handy when you want a little more firepower and flexibility, such as the calculation of a value. Consider the following XML file in Listing 8-1.

Listing 8-1: films.xml

<films>

  <film name=”Henry V”>

     <director>Kenneth Branagh</director>

     <writer>Kenneth Branagh, William Shakespeare</writer>

     <year>1989</year>

     <runtime>137</runtime> 

     <sound>Stereo</sound>

     <genre>Drama</genre>

     <score>10.0</score>

     <mpaa>PG-13</mpaa>

  </film>

  <film name=”Groundhog Day”>

     <director>Harold Ramis</director>

     <writer>Danny Rubin</writer>

     <year>1993</year>

     <runtime>101</runtime> 

     <sound>Dolby</sound>

     <genre>Romantic Comedy</genre>

     <score>9.0</score>

     <mpaa>PG</mpaa>

  </film> 

  <film name=”Man for All Seasons”>

     <director>Fred Zinnemann</director>

     <writer>Robert Bolt</writer>

     <year>1966</year>

     <runtime>120</runtime> 

     <sound>Mono</sound>

     <genre>Drama</genre>

     <score>8.0</score>

     <mpaa>PG</mpaa>

  </film> 

  <film name=”Field of Dreams”>

     <director>Phil Alden Robinson</director>

     <writer>W.P.Kinsella, Phil Alden Robinson</writer>

     <year>1988</year>

     <runtime>107</runtime> 

     <sound>Dolby</sound>

     <genre>Drama</genre>

     <score>9.8</score>

     <mpaa>PG</mpaa>

  </film> 

  <film name=”Babette&apos;s Feast”>

     <director>Gabriel Axel</director>

     <writer>Gabriel Axel</writer>

     <year>1987</year>

     <runtime>102</runtime> 

     <sound>Dolby</sound>

     <genre>Drama</genre>

     <score>9.5</score>

     <mpaa>PG</mpaa>

  </film> 

</films>

Calculate the total number of PG-rated films in the list and then assign that value to a variable. You can tackle that problem by first defining a variable that uses an XPath expression to return the desired films.

<xsl:variable name=”pgFilms” select=”//film[mpaa=’PG’]”/>

The pgFilms variable uses the //film[mpaa=’PG’] expression to tell the processor, “Gimme all the <film> elements in the document that have an <mpaa> child with the value of PG.”

The pgFilms variable differs from others in that its value is not a string or number but rather a result tree fragment, or a collection of nodes that meet the stated requirements.

After you have a variable to represent the tree nodes, your next step is to declare a second variable that takes the value of the first to calculate the total number of PG films.

<xsl:variable name=”totalNumOfPgFilms” select=”count($pgFilms)”/>

In the preceding expression, the built-in XPath count() function adds up the total number of nodes contained in the result tree fragment stored in the pgFilms variable.

Keeping the output simple, you can use the variable like this:

<xsl:variable name=”pgFilms” select=”//film[mpaa=’PG’]”/>

<xsl:variable name=”totalNumOfPgFilms” select=”count($pgFilms)”/>

<xsl:template match=”/”>

   <p>The total number of PG films: 

   <xsl:value-of select=”$totalNumOfPgFilms”/>

   </p>

</xsl:template>  

The end result in HTML is

The total number of PG films: 4

Think Globally, Act Locally

You’ve undoubtedly seen the slogan Think globally, act locally on bumper stickers. My personal favorite bumper sticker is Visualize Whirled Peas, but flying veggies are quite distracting when you try to work with variables, so I’ll opt for the more environmentally sound expression.

Although the Think globally, act locally slogan is appropriate for conserving nature, it’s also an ideal maxim for using variables because all variables have scope. A variable’s scope is the area of the stylesheet in which the variable is available for use. Variables can be available to the entire document (globally) or simply to a specific region (locally).

Throughout previous chapters of this book, I define variables at the beginning of the XSLT stylesheets and then use them in templates defined at the same tree level. When you declare a variable at the top level of a document, however, you define a global variable, meaning that you can use this variable anywhere in the stylesheet and that it’s always available.

You can also define variables inside of a given template rule. When you do so, this local variable is available to only that template rule or any elements that are inside of it. For example, the variable thinkGlobally can be used anywhere in this stylesheet:

<xsl:variable name=”thinkGlobally”>14pt</xsl:variable>

<xsl:template match=”film”>

   <font size=”{$thinkGlobally}”>

     <xsl:apply-templates select=”writer”/>

   </font><xsl:text>

  </xsl:text>

   <xsl:variable name=”actLocally”>12pt</xsl:variable>

   <font size=”{$actLocally}”>

     <xsl:apply-templates select=”director”/>

   </font>

</xsl:template>

to output the following HTML:

<font size=”14”>Kenneth Branagh, William Shakespeare</font>

<font size=”12”>Kenneth Branagh</font>

Although globals can be used anywhere, the actLocally variable can be used only inside the template rule in which it is defined. If you try to use it outside of the template rule it is in, the XSLT processor yells at you.

A second issue to consider when scoping variables is precedence when two variables have identical names. In other words, who wins the duel? In XSLT, the variable farthest down the hierarchy always wins; a local variable always overrides its global counterpart. So if you tweak the example and change the name of the local variable to also be thinkGlobally, the XSLT processor gives the writer element a value of 14pt and the director element a value of 12pt:

<xsl:template match=”film”>

   <font size=”{$thinkGlobally}”>

     <xsl:apply-templates select=”writer”/>

   </font><xsl:text>

  </xsl:text>

   <xsl:variable name=”thinkGlobally”>12pt</xsl:variable>

   <font size=”{$thinkGlobally}”>

     <xsl:apply-templates select=”director”/>

   </font>

</xsl:template>

with identical output as before:

<font size=”14”>Kenneth Branagh, William Shakespeare</font>

<font size=”12”>Kenneth Branagh</font>

Through these examples, you can see that Think globally, act locally holds true. Although you need to be aware of global variables that affect the stylesheet as a whole, in the end, the value of the variable being used depends wholly on the local conditions in the template rule in which it’s being used.

Working with Parameters

XSLT packs another handy device called a parameter, which is a close cousin to a variable. Parameters follow the same general behavior of variables, but they have the added flexibility of being able to change their values at the time that the XSLT stylesheet is processed.

How parameters resemble variables

A parameter is defined in XSLT by using the xsl:param element and follows the same syntax rules of variables. Therefore, to define a content-based parameter, the name attribute of xsl:param specifies the parameter name and the text between the start and end tags provides its value.

<xsl:param name=”style”>Normal</xsl:param>

Or, to define a select attribute parameter, put the value inside the select attribute of xsl:param.

<xsl:param name=”style” select=”’Normal’”/>

Think of parameters as a superset of variables because they do everything that variables do — and more. For each of the preceding examples in this chapter, you can substitute xsl:param in place of xsl:variable and achieve the exact same results.

How parameters are different from variables

Parameters can be used in place of variables, but their main purpose is to provide a way to override default values at run-time, both from inside and outside of XSLT stylesheets.

Overriding parameters with the with-param element

To override the value of a parameter within a stylesheet, use the xsl:with-param element. This instruction is added to a template rule, mirroring the variable and parameter elements.

<xsl:with-param name=”style”>Enhanced</xsl:with-param>

Or you can add it like this:

<xsl:with-param name=”style” select=”’Enhanced’”/>

The typical use of xsl:with-param is to override a parameter that’s been defined in a named template. To help you understand this, look at an example. Begin with the following XML fragment.

  <film name=”Field of Dreams”>

    <director>Phil Alden Robinson</director>

    <writer>W.P.Kinsella, Phil Alden Robinson</writer>

    <year>1988</year>

    <runtime>107</runtime>

    <sound>Dolby</sound>

    <genre>Drama</genre>

    <score>9.8</score>

    <mpaa>PG</mpaa>

  </film>

My objective is to generate an HTML document that transforms the values of the director, writer, and year elements and turns them into links (a elements), giving each link a unique URL.

<p><a href=”legend.html#director”>Phil Alden Robinson</a></p>

    <p><a href=”legend.html#writer”>W.P.Kinsella, Phil Alden Robinson</a></p>

    <p><a href=”legend.html#year”>1988</a></p>

Parameters are well suited for handling such a task because they allow you to change their value during the transformation.

Start by creating a named template called labels that contains a parameter value called legendUrl. The labels template defines the a element and the spot where the legendUrl parameter is plugged in.

<!-- Labels named template --> 

<xsl:template name=”labels”>

  <xsl:param name=”legendUrl”>legend.html#main</xsl:param>

  <a href=”{$legendUrl}”><xsl:apply-templates/></a>

</xsl:template>

After you define the labels template, add template rules for the director, writer, and year elements. For each of these, use an xsl:call-template instruction to call the labels template. Nestled comfortably inside of xsl:call-template is an xsl:with-param element that sends a new value to the legendUrl parameter. This value is then used as the href value in the resulting HTML.

<!-- Director --> 

<xsl:template match=”director”>

  <xsl:call-template name=”labels”>

    <xsl:with-param name=”legendUrl”>legend.html#director</xsl:with-param>

  </xsl:call-template>

</xsl:template>

<!-- Writer --> 

<xsl:template match=”writer”>

  <xsl:call-template name=”labels”>

    <xsl:with-param name=”legendUrl”>legend.html#writer</xsl:with-param>

  </xsl:call-template>

</xsl:template>

<!-- Year --> 

<xsl:template match=”year”>

  <xsl:call-template name=”labels”>

    <xsl:with-param name=”legendUrl”>legend.html#year</xsl:with-param>

  </xsl:call-template>

</xsl:template>

<!-- Remove these elements from our results document --> 

<xsl:template match=”sound”/>

<xsl:template match=”genre”/>

<xsl:template match=”mpaa”/>

<xsl:template match=”runtime”/>

<xsl:template match=”score”/>

See Figure 8-2 for the formatted results of the transformation.

Figure 8-2: Using parameters allows values to be updated during transformation.

Figure 8-2: Using parameters allows values to be updated during transforma-tion.

Overriding parameters from the XSLT processor

An invisible boundary for your XSLT work area throughout this book is the stylesheet itself; all the XSLT elements and instructions are contained inside the stylesheet document. However, parameters have the ability to be changed from The Great Beyond — outside the stylesheet — by passing in a new value from the XSLT processor. Exactly how you do this depends on the specific XSLT processor that you use.

For example, by using the SAXON processor, you can specify a parameter name-value pair as a command line parameter.

saxon xmlfile.xml xslfile.xsl param=value

If you want to pass in a value for a parameter called myfontsize, the command line is

saxon films.xml param2.xsl myfontsize=2

The XSLT processor passes the value of 2 to the myfontsize parameter, overriding any default values that were originally provided in the param2.xsl stylesheet.

Tip

Some XSLT processors may use a different method to pass parameter values to a stylesheet, but they all give you an identical result in the output document.

Much of the power of parameters lies in their ability to be changed from the outside. In so doing, parameters give you freedom to use values in your transformation that may not be known at the time when you’re actually writing the XSLT stylesheet. Using parameters also enables you to run new transformations without changing the underlying XSLT stylesheet one iota.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.154.18