Day 8
Working with Variables

Yesterday you learned to manipulate the actual output that is created with elements and functions covered in the days before it. You learned how to determine the output type and how to control whitespace. In a sense, yesterday’s lesson had nothing to do with processing, but rather how the processed output actually came out of the processor.

Today’s lesson again covers the processing side of XSLT, specifically how to deal with complex situations in which expressions can become very complex, so complex that they are hard to create and understand. Another topic that will be revisited is the way the current context affects expressions and ways to get around that problem.

Today you will learn the following:

• What variables are

• How to create and use variables

• How variables can help you to make stylesheets less complex

• How variables solve problems with the current context

Understanding Variables

In all the stylesheets you’ve created so far, you have been operating on the source XML document as a whole, based on the current context. That situation is not ideal if you want to create complex output, in which some of the output might depend on data that is out of context. By that, I mean operating on the output without a complex expression is not easy, and in some cases, it’s hardly possible. Variables are a mechanism in XSLT designed to help in these situations.

What Are Variables?

If you have experience with programming, you’re probably thinking, “Hey, I know what variables are.…” If that’s the case, you’re in for a surprise. Variables in most programming languages and variables in XSLT are not quite the same. They have similarities but some radical differences as well because variables in XSLT are more closely related to mathematical variables than variables in common programming languages. In a mathematical context, a variable is nothing more than a name given to a certain value. If you, for instance, have an equation such as

3x - 9 = 0

x is a variable. The term variable in such an equation is a bit misleading because this equation has only one solution: x is 3. So, the variable x just denotes the value 3 and cannot change unless the whole equation is changed.

Variables in XSLT are similar to mathematical variables in that they can be declared and given a value. After that value is given, the variable can no longer change; its value is fixed. In contrast, a variable in common programming languages can be changed after it has been declared and given a value, as done in the following code:

var x = 3;
var y = 4;
document.write (x);
x = y * 4;
document.write (x);

This code declares two variables and gives them a value. Then it writes the value of variable x to the output. Next, the variable x is given a new value, which is subsequently written to the output again. So, you see that a variable in this case does not have a fixed value. In fact, many programming constructs rely on this principle. A good example is a code snippet shown on Day 6, “Conditional and Iterative Processing,” which is as follows:

For i = 1 To 10
     'execute some code
Next

This code contains a variable i, which increases in value each time the code iterates. In this case, the variable increases 10 times, from 1 to (and including) 10.

Programming constructs like the ones shown in the preceding examples are not available in XSLT. Part of the reason is that XSLT is a declarative programming language: You tell the processor what you want, not how you want it to happen. When you tell the processor that you want each element in a node-set processed, it does so, no matter how many nodes are in the node-set. Also, you don’t have to figure out how many nodes there are and then tell the processor to do something a certain number of times, giving it the data you want it to process in each iteration. So, when XSLT was designed, there was good reason to leave out these constructs.

What Is the Benefit of Variables?

Because variables in XSLT are different from variables in other languages, you might question their usefulness. However their use is different does not mean they are insignificant. In the following paragraphs, I’ll describe most of the advantages of variables. This discussion is still a theoretical overview, but later in this lesson, you will see these points in action.

Variables can make your code much easier to read. Instead of repeating a complex expression to select a value, you can use that expression once to create a variable with a meaningful name. Wherever you need to use the value from the expression, you now can use the variable instead. Using variables this way, of course, makes your code much easier to read and understand because you quickly know from the variable name what you are dealing with. If, instead, you need to use the complex expression, figuring out what is actually happening might prove difficult. Another benefit of this approach is that you can break a complex expression into pieces and build it in steps using several variables. With each step, you know exactly what you are dealing with, because the expression in each stage is much simpler and therefore much less error prone.

If you reuse a variable many times in a stylesheet, it also is likely to increase the performance of that stylesheet, particularly when the expression you use is complex and the resulting value is a node-set or tree fragment. Most processors store a reference or references to the node or nodes that are the result of an expression that is stored in a variable. This means that the expression does not have to be evaluated again when the variable is used, probably saving execution time.

You also can use variables to define values in a central location, so you can change the values easily. For example, you might use some value for formatting a particular type of element. Although, in many cases, you also could use attribute-sets to do so, in some cases, this approach is easier or more beneficial.

Finally, you can use variables to hold values of nodes that are relevant to the current operation but are not accessible from the context node. This is particularly the case when you have nested operations in which the context node changes along with the nesting. You might need to operate on nodes that are not in context quite often, especially if you have to compare values of different nodes with the value of the context node. Without the ability to store the value of a node that is inaccessible from the current context, such a comparison might be hard to achieve.

Creating and Using Variables

With some of the theory of variables under your belt, it’s time to look at the actual thing. As this section progresses, the different uses discussed in the preceding section will become more apparent.

Using Simple Variables

As I said, you can create variables that contain a node-set or tree fragment. Because these types of variables are harder to work with than variables containing only a single value, the focus will first be on the latter. I will refer to variables containing a single value, such as a string or Boolean value, as a simple variable.

A simple variable is most likely used to insert values at several places in a document, with a central location to change that value. You can create a simple value in two different ways. The easiest method is to use the following:

<xsl:variable name="somename">somevalue</xsl:variable>

You also can create the same variable by using the following:

<xsl:variable name="somename" select="'somevalue'" />

The apostrophe characters in the second method are needed to tell the processor that the value is a string. If you leave out the apostrophes, the value of the variable will likely be empty because you are telling the processor that it needs to use the value from the somevalue element within the current context. In all likelihood, that element does not exist. I strongly recommend using the former method because mistakes are hardly possible with it. Throughout this book, I will use the former method wherever it is applicable.

After you create a variable, you can access it through its name, preceded by a $ character. So, to output the value of a variable, you use the following code:

<xsl:value-of select="$somename" />

The best way to see how a variable fits in a stylesheet is to look at an example. Consider Listing 8.1, which is a listing you should almost know by heart by now.

LISTING 8.1 Sample XML Document

<?xml version=″“1.0”" encoding="UTF-8"?>
<cars>
  <car model="Focus" manufacturer="Ford" year="2000" />
  <car model="Golf" manufacturer="Volkswagen" year="1999" />
  <car model="Camry" manufacturer="Toyota" year="1999" />
  <car model="Civic" manufacturer="Honda" year="2000" />
  <car model="Prizm" manufacturer="Chevrolet" year="2000" />
</cars>

Note

You can download the sample listings in this lesson from the publisher’s Web site.

Suppose you want to create an HTML table from the XML source in Listing 8.1, and you want the row colors to alternate. You could, of course, hard-code color codes into the template dealing with the creation of the table rows. If you have an elaborate site layout, however, those colors are likely to be used in other sections of the layout as well—for instance, in a frame around the table or page. If you want to change those colors, you have to go into each template that uses the colors and physically change them. If the colors are stored in variables, you have to change the variables in only one central location. Listing 8.2 shows a stylesheet that uses variables to store the colors.

LISTING 8.2 Stylesheet Using Variables for Color Management

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="html" version="4.0" />
6:
7:    <xsl:variable name="bgcolor">#cccccc</xsl:variable>
8:    <xsl:variable name="altbgcolor">#ffffff</xsl:variable>
9:
10:   <xsl:template match="/">
11:     <html>
12:     <body bgcolor="{$bgcolor}">
13:       <xsl:apply-templates />
14:      </body>
15:      </html>
16:   </xsl:template>
17:
18:   <xsl:template match="cars">
19:     <table bgcolor="{$altbgcolor}" width="75%">
20:       <xsl:for-each select="car">
21:         <tr>
22:           <xsl:attribute name="bgcolor">
23:             <xsl:choose>
24:               <xsl:when test="position () mod 2 = 0">
25:                 <xsl:value-of select="$altbgcolor" />
26:                </xsl:when>
27:                <xsl:when test="position () mod 2 = 1">
28:                 <xsl:value-of select="$bgcolor" />
29:                </xsl:when>
30:              </xsl:choose>
31:            </xsl:attribute>
32:           <xsl:call-template name="car" />
33:         </tr>
34:       </xsl:for-each>
35:      </table>
36:   </xsl:template>
37:
38:   <xsl:template name="car">
39:     <td><xsl:value-of select="@model" /></td>
40:      <td><xsl:value-of select="@manufacturer" /></td>
41:      <td><xsl:value-of select="@year" /></td>
42:   </xsl:template>
43: </xsl:stylesheet>

ANALYSIS

NEW TERM

In Listing 8.2, two variables are created on lines 7 and 8: bgcolor and altbgcolor. The variables contain color values for both background color and alternate background color used when the table element is inserted on line 19, and again on lines 25 and 28 with the tr element. Note that when these elements are created, the attribute values are given inside curly braces because they are dynamic values based on an expression. That expression is only a variable, but it is an expression, nonetheless. The variables are created as top-level elements. This means that these variables are global. A global variable is available in the entire document.

On line 12, the variable bgcolor gives a value to the bgcolor attribute of the body element. The variable is inserted between curly braces to make sure it is processed as a variable, not as plain text. After the variable is processed, the output will contain the value of the variable, not $somename. On line 19, the variable altbgcolor is used in the same way to give a value to the bgcolor attribute of the table element.

Lines 22–31 are also significant. In that part of the code, a bgcolor attribute for the tr element on line 21 is created dynamically. For each car element in the source XML, a tr element is created. The value of the bgcolor attribute depends on whether the position of the context node within the node-set, selected by the xsl:for-each element on line 20, is even or odd. This is tested using the position () mod 2 = expression on line 24, which returns true if position () returns an even number. Its counterpart on line 27 returns true if position () is odd. When the value of position () is even, the value of the bgcolor attribute is the value of the altbgcolor variable, which is inserted on line 25. Otherwise, line 28 inserts the value of the bgcolor variable.

Line 32 calls a template to insert the cells in the table row. This template is called so that the template matching the cars element deals as much with inserting the differently colored table rows as possible. The result from applying Listing 8.2 to Listing 8.1 is shown in Listing 8.3.

OUTPUT

LISTING 8.3 Result from Applying Listing 8.2 to Listing 8.1

1:  <html>
2:     <body bgcolor="#cccccc">
3:        <table bgcolor="#ffffff" width="75%">
4:            <tr bgcolor="#cccccc">
5:              <td>Focus</td>
6:               <td>Ford</td>
7:               <td>2000</td>
8:            </tr>
9:            <tr bgcolor="#ffffff">
10:              <td>Golf</td>
11:              <td>Volkswagen</td>
12:              <td>1999</td>
13:           </tr>
14:           <tr bgcolor="#cccccc">
15:              <td>Camry</td>
16:             <td>Toyota</td>
17:              <td>1999</td>
18:           </tr>
19:           <tr bgcolor="#ffffff">
20:              <td>Civic</td>
21:              <td>Honda</td>
22:              <td>2000</td>
23:           </tr>
24:           <tr bgcolor="#cccccc">
25:              <td>Prizm</td>
26:             <td>Chevrolet</td>
27:              <td>2000</td>
28:           </tr>
29:        </table>
30:    </body>
31: </html>

ANALYSIS

In Listing 8.3, the bgcolor attribute of the body element on line 2 holds the value of the bgcolor variable from Listing 8.2. Also, the table element on line 3 has a bgcolor attribute with the value of the altbgcolor variable in Listing 8.2. Finally, the table rows have alternating background colors which you can see even more clearly in Figure 8.1.

FIGURE 8.1 The result in Listing 8.3 when viewed in a browser.

Image

Simple variables are handy tools to reduce work when you have to change certain values in your output. You can create elements and attributes with certain values, or even elements and attributes themselves, based on a simple variable value. This way, your stylesheets can be very flexible.

Using Complex Variables

As I said earlier, you are not limited to using simple variables. They also can contain a node-set or tree fragment. I will therefore refer to them as complex variables as opposed to single-valued simple variables. Complex variables can be defined within a stylesheet itself, just like the bgcolor variable in Listing 8.2, but they also can be created with a select expression selecting a node-set or tree fragment in the source XML. This section will concentrate on defining variables in a stylesheet. Later in this lesson, you will learn how to create variables using a select expression.

Complex variables are particularly useful when you want to group values together. You can easily create such variables just by creating an XML structure inside the xsl:variable element. You cannot use the select attribute of the xsl:variable element to create such a value, as is possible with simple values, because the select attribute is incapable of holding XML data. You can use the select attribute only to create simple variables or variables containing data from the XML source created from a select expression. Listing 8.4 shows how to create and use a complex variable.

LISTING 8.4 Stylesheet with a Complex Variable

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="html" version="4.0" />
6:
7:    <xsl:variable name="bgcolor">
8:      <body>#cccccc</body>
9:       <table>#ffffff</table>
10:     <row>#cccccc</row>
11:     <altrow>#ffffff</altrow>
12:    </xsl:variable>
13:
14:    <xsl:template match="/">
15:     <html>
16:     <body bgcolor="{$bgcolor/body}">
17:       <xsl:apply-templates />
18:      </body>
19:      </html>
20:    </xsl:template>
21:       22:   <xsl:template match="cars">
23:     <table bgcolor="{$bgcolor/table}" width="75%">
24:       <xsl:for-each select="car">
25:          <tr>
26:           <xsl:attribute name="bgcolor">
27:             <xsl:choose>
28:                <xsl:when test="position () mod 2 = 0">
29:                 <xsl:value-of select="$bgcolor/altrow" />
30:                </xsl:when>
31:                <xsl:when test="position () mod 2 = 1">
32:                 <xsl:value-of select="$bgcolor/row" />
33:                </xsl:when>
34:              </xsl:choose>
35:           </xsl:attribute>
36:           <xsl:call-template name="car" />
37:         </tr>
38:       </xsl:for-each>
39:      </table>
40:   </xsl:template>
41:
42:   <xsl:template name="car">
43:     <td><xsl:value-of select="@model" /></td>
44:      <td><xsl:value-of select="@manufacturer" /></td>
45:     <td><xsl:value-of select="@year" /></td>
46:   </xsl:template>
47: </xsl:stylesheet>

ANALYSIS

Listing 8.4 isn’t much different from Listing 8.2. However, instead of creating multiple variables, one variable called bgcolor is created on line 7. It contains several elements, with colors defined for the different HTML elements, similar to a Cascading Stylesheet. On line 16, the bgcolor attribute is now set to the value of the body element within the bgcolor variable. Similarly, on line 23, the background color of the table is set to the value of the table element in the variable. Lines 29 and 32 insert the alternating background colors for the table rows.

You can create multiple variables, both simple and complex. Using complex variables, you can create groups of values that are used for the same purpose or for the same element or elements. In some cases, you could use attribute-sets to achieve the same result. Whether variables or attribute-sets are the best solution depends on the situation.

Creating Variables from Expressions

In the preceding sections, you learned how to create both simple and complex variables by hard-coding their values in the stylesheet. This way, you can store information relevant to the transformation in a central location, which enables you to easily edit common values. In some situations, you need to create variables that are based on the source XML. Such values can again be simple values, but also node-sets or tree fragments, depending on the select expression used. You can create these variables only by using the select attribute of the xsl:variable element. The expression used in the select attribute is used to select data from the source XML.

Using Variables to Replace an Expression

Expressions addressing data in the XML source can become very complex and very long. If that is the case, storing an expression’s result in a variable is often helpful, especially if you have to use that expression in more than one place. If you give the variable a descriptive name, everybody can understand what the expression selects, without having to read or understand the expression itself. As I said previously, the processor does not have to evaluate the expression again if the variable is used again, so there also might be a slight performance benefit.

On Day 3, “Selecting Data,” you learned how to create a nice-looking menu from an XML source with the data for that menu. Listing 8.5 shows the source XML used on Day 3.

LISTING 8.5 Sample XML Source with Menu Data

<?xml version=″“1.0”" encoding="UTF-8"?>
<menu>
  <appetizers title="Work up an Appetite">
    <dish id="1" price="8.75">Crab Cakes</dish>
    <dish id="2" price="9.95">Jumbo Prawns</dish>
    <dish id="3" price="10.95">Smoked Salmon and Avocado Quesadilla</dish>
    <dish id="4" price="6.95">Caesar Salad</dish>
  </appetizers>
  <entrees title="Chow Time!">
    <dish id="5" price="19.95">Grilled Salmon</dish>
    <dish id="6" price="17.95">Seafood Pasta</dish>
    <dish id="7" price="16.95">Linguini al Pesto</dish>
    <dish id="8" price="18.75">Rack of Lamb</dish>
    <dish id="9" price="16.95">Ribs and Wings</dish>
  </entrees>
  <desserts title="To Top It Off">
    <dish id="10" price="6.95">Dame Blanche</dish>
    <dish id="11" price="5.95">Chocolate Mousse</dish>
    <dish id="12" price="6.95">Banana Split</dish>
  </desserts>
</menu>

Suppose you want to create a menu that also shows this week’s menu, which has to vary every week. You could create this menu by hand, but you also could automate the task somewhat. If you want to do that, the expressions very likely will become quite complex. Listing 8.6 shows that this is indeed the case and that you can use variables to make the stylesheet less complex.

LISTING 8.6 Stylesheet Using Selected Variable

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="text" />
6:
7:    <xsl:variable name="week">7</xsl:variable>
8:
9:    <xsl:variable name="weekappetizer"
10:        select="/menu/appetizers/dish[position () =
11:         ( ( ($week - 1) mod count (/menu/appetizers/dish)) + 1)]" />
12:
13:   <xsl:variable name="weekentree"
14:        select="/menu/entrees/dish[position () =
15:         ( ( ($week - 1) mod count (/menu/entrees/dish)) + 1)]" />
16:
17:   <xsl:variable name="weekdessert"
18:         select="/menu/desserts/dish[position () =
19:         ( ( ($week - 1) mod count (/menu/desserts/dish)) + 1)]" />
20:
21:  <xsl:template match="/">
22:      <xsl:text>This week's menu:&#xA;</xsl:text>
23:     <xsl:text>- </xsl:text><xsl:value-of select="$weekappetizer" />
24:     <xsl:text> $</xsl:text><xsl:value-of select="$weekappetizer/@price" />
25:     <xsl:text>&#xA;- </xsl:text><xsl:value-of select="$weekentree" />
26:     <xsl:text> $</xsl:text><xsl:value-of select="$weekentree/@price" />
27:     <xsl:text>&#xA;- </xsl:text><xsl:value-of select="$weekdessert" />
28:     <xsl:text> $</xsl:text><xsl:value-of select="$weekdessert/@price" />
29:   </xsl:template>
30: </xsl:stylesheet>

ANALYSIS

Listing 8.6 defines four variables, one with a hard-coded value and the others defined from an expression depending on both the source XML and the hard-coded variable. On line 7, a variable named week is defined and given a value. This value is the number of the current week within the year. As you can see, the current week is defined as week 7. On line 9, a variable named weekappetizer is created using a complex expression that calculates which appetizer is this week’s appetizer, based on the number in the week variable. On lines 13 and 17, the same thing is done to get an entree and a dessert. The three variables that result from these expressions are used in the template on line 21. Each variable is used twice, once to get the name of the dish and once to get the price. If you didn’t have variables, you would have to use the expressions used to create the variables in both instances, which would make the template more complex. Also, with the variables, you can easily understand what is happening because the variables have understandable names. Without variables, it is much harder to grasp what’s actually going on.

Let’s look at the expressions for a moment to see what’s actually happening there. I’ll concentrate on the expression creating the weekappetizer variable, but the idea is the same for all of them. The expression selects all dish elements that are child elements of the appetizers element. A predicate is used to select the dish element that is the appetizer for this week. The number is calculated by taking the current week and dividing that by the number of appetizers. The resulting number is the appetizer for this week. The number of appetizers is calculated with the count () function, which returns the number of nodes in the given node-set. The mod operator determines the remaining number after the number to the left has been divided by the number to the right. Both of these numbers are the result of an expression, so it looks more complex than it is. To make sure that week 1 will always yield a menu that consists of the first appetizer, the first entree, and the first dessert in the source document, the expression is adjusted by adding or subtracting 1. The result of the expression is that on week 1 the first appetizer will be selected; on week 2, the second appetizer; and so on. On week 5, when all appetizers have been selected once, the selection starts at the beginning of the list again. The same expression is used for the entrees and desserts, and because there are four appetizers, five entrees, and three desserts, the menu will be unique for many weeks to come.

If the expressions are still too complex for your taste, you can divide them into pieces as well, as shown in Listing 8.7.

LISTING 8.7 Replacement Code for Lines 9–11 in Listing 8.6

<xsl:variable name="correctedweek" select="$week - 1" />
<xsl:variable name="appetizercount"
  select="count (/menu/appetizers/dish)) + 1" />
<xsl:variable name="appetizerpos" select="$correctedweek
 mod $appetizercount" />
<xsl:variable name="weekappetizer"
  select="/menu/appetizers/dish[$appetizerpos]" />

The result of the preceding code is the same as lines 9–11 of Listing 8.6. Because the expressions are broken into smaller pieces, the complexity of the expression is as well. Each step of building the result is easy to understand, and the result is the same. That said, if you are able to understand and write the original expression, your code is cleaner because it isn’t cluttered with variables. That, in itself, might make your code more readable. When and if you break up an expression is a matter of judgment.

Apart from Listing 8.6 being easier to read, it is very likely that using variables is faster because the expressions have to be evaluated only once, instead of once for the name of the dish and once for the price. Applying Listing 8.6 to Listing 8.5 yields the result in Listing 8.8.

OUTPUT

LISTING 8.8 Result from Applying Listing 8.6 to Listing 8.5

This week's menu:
- Smoked Salmon and Avocado Quesadilla $10.95
- Seafood Pasta $17.95
- Dame Blanche $6.95

ANALYSIS

In Listing 8.8, week 7 yields the third appetizer, the second entree, and the first dessert from Listing 8.5. If you take the time to count through each week, you will find that this is exactly the output that should be created.

Using Variables for Out-of-Context Data

When you’re working with templates and iterations, you likely will be nesting elements, digging deeper into the XML source. As long as you’re digging into one part of the XML tree structure, there is no problem in addressing elements higher up in the nesting structure. However, if you have two related structures that do not have a parent-child or parent-descendant relationship, just selecting data from a location higher in the nesting structure does not yield the expected result. To make this point clearer, look at Listing 8.9.

LISTING 8.9 Sample XML Document with Separate Data Sets

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <cars>
3:    <models>
4:<model name="Golf" manufacturer="VW" year="1999" />
5: <model name="Camry" manufacturer="TY" year="1999" />
6: <model name="Focus" manufacturer="FO" year="2000" />
7: <model name="Civic" manufacturer="HO" year="2000" />
8: <model name="Prizm" manufacturer="CV" year="2000" />
9: <model name="Celica" manufacturer="TY" year="2000" />
10:<model name="Mustang" manufacturer="FO" year="2001" />
11;<model name="Passat" manufacturer="VW" year="2001" />
12:<model name="Accord" manufacturer="HO" year="2002" />
13:<model name="Corvette" manufacturer="CV" year="2002" />
14:   </models>
15:   <manufacturers>
16:     <manufacturer id="VW" name="Volkswagen" country="Germany" />
17:<manufacturer id="TY" name="Toyota" country="Japan" />
18:     <manufacturer id="FO" name="Ford" country="USA" />
19:<manufacturer id="CV" name="Chevrolet" country="USA" />
20:     <manufacturer id="HO" name="Honda" country="Japan" />
21:   </manufacturers>
22: </cars>

ANALYSIS

Listing 8.9 contains two major sets of data. One set is formed by model elements, grouped by the models element on line 3. The other data set is a set of manufacturer elements, grouped by the manufacturers element on line 15. Although the models and manufacturers elements are related, they have a sibling relationship, not a parent-descendant relationship. If you iterate through one of the datasets and, within that, iterate through related data from the other set, getting to the data from the outer iteration is tricky at best. The more iterations that are nested, the harder it becomes to get to the data. However, if you store information from the current iteration in a variable and then start a new iteration nested within the current one, you can access the data from the outer iteration by using the variable that was created.

For example, this situation might happen when you want to make a list of manufacturers, with each manufacturer displaying the cars that it produces. The outer iteration then loops through the manufacturers, with the inner iteration going through the cars belonging to the current manufacturer. Listing 8.10 does exactly that.

LISTING 8.10 Stylesheet with a Nested Iteration

1:  <?xml version=″“1.0”" encoding="UTF-8"?>
2:  <xsl:stylesheet version=″“1.0”"
3:    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
4:
5:    <xsl:output method="html" version="4.0" />
6:
7:    <xsl:template match="/">
8:      <html>
9:      <body>
10:       <h1>Auto show</h1>
11:       <xsl:apply-templates select="/cars/manufacturers" />
12:     </body>
13:     </html>
14:   </xsl:template>
15:
16:   <xsl:template match="manufacturers">
17:     <xsl:for-each select="manufacturer">
18:       <h2><xsl:value-of select="@name" /></h2>
19:       <p><i>Country: <xsl:value-of select="@country" /></i></p>
20:       <xsl:variable name="mfc" select="." />
21:       <xsl:for-each select="/cars/models/model[@manufacturer = $mfc/@id]">
22:         <ul>
23:           <li>
24:             <xsl:value-of select="@name" />
25:             <xsl:text>  (</xsl:text>
26:              <xsl:value-of select="@year" />
27:              <xsl:text>)</xsl:text>
28:             </li>
29:           </ul>
30:         </xsl:for-each>
31:       </xsl:for-each>
32:     </xsl:template>
33:   </xsl:stylesheet>

ANALYSIS

In Listing 8.10, the template matching the manufacturers element beginning on line 16 is the place where all the action is. On line 17 an xsl:for-each element is used to iterate through all the manufacturer elements. That is the outer iteration. Line 18 writes the name of the manufacturer, and line 19 writes the country it is from. On line 20, a variable named mfc (short for manufacturer) is created. The value of this variable is set to the current manufacturer element. With each outer iteration, the value of the variable is changed to the current manufacturer element and is then used on line 21 to select only those cars that have the same manufacturer as the current manufacturer. The result from applying Listing 8.10 to Listing 8.9 is shown in Listing 8.11.

OUTPUT

LISTING 8.11 Result from Applying Listing 8.10 to Listing 8.9

<html>
<body>
   <h1>Auto show</h1>
   <h2>Volkswagen</h2>
   <p><i>Country: Germany</i></p>
   <ul>
     <li>Golf  (1999)</li>
   </ul>
   <ul>
     <li>Passat  (2001)</li>
   </ul>
   <h2>Toyota</h2>
   <p><i>Country: Japan</i></p>
   <ul>
     <li>Camry  (1999)</li>
   </ul>
   <ul>
  <li>Celica  (2000)</li>
</ul>
<h2>Ford</h2>
<p><i>Country: USA</i></p>
<ul>
  <li>Focus  (2000)</li>
</ul>
<ul>
  <li>Mustang  (2001)</li>
</ul>
<h2>Chevrolet</h2>
<p><i>Country: USA</i></p>
<ul>
  <li>Prizm  (2000)</li>
</ul>
<ul>
  <li>Corvette  (2002)</li>
</ul>
<h2>Honda</h2>
<p><i>Country: Japan</i></p>
<ul>
  <li>Civic  (2000)</li>
</ul>
<ul>
  <li>Accord  (2002)</li>
</ul>
</body>
</html>

Note

In Listing 8.10, variables are not strictly needed. Instead of the variable, you could use the current () function in the select expression on line 21. However, when the nesting is deeper, you need to use variables to get to data from higher nestings.

When you view Listing 8.11 in a browser, the result looks like Figure 8.2.

FIGURE 8.2 The result in Listing8.11 when viewed in a browser.

Image

The scope of a variable is the area in which it is available. Outside that scope, the variable does not exist.

The boundaries of the scope are determined by the parent element and the position of the xsl:variable element. The variable is available only within the parent element, which means that it is available only to its sibling elements and their descendants. Also, it is available to siblings only after the variable’s definition. In other words, the variable’s scope is the following axis. With a template or with an xsl:for-each iteration, the variable created inside the template body (or the body of the xsl:for-each element) is recreated each time the template is matched or a new iteration is started. So, the variable in Listing 8.10 doesn’t really change; it is actually re-created for each iteration.

The scope of a variable is not limited to templates and iterations. A variable declared within the body of an xsl:if element is available only to the following axis within the xsl:if element, not outside it. Variables declared in different parts of the stylesheet can therefore have the same name, as long as their scopes do not overlap. When the scope of two variables overlaps and they have the same name, the variable created in the broadest scope is not accessible. Say you create a global variable and a variable within a certain template that has the same name. Within that template, only the variable created there is available; the global variable is not. Figure 8.3 shows several variable declarations and shows you where the declared variables are in scope.

FIGURE 8.3 Scope of different variables.

Image

Note

So that you have a clear picture of the scope of the variables in Figure 8.3, superfluous elements and attributes have been removed.

Creating Variables from XSLT Elements

Instead of using the select attribute, you also can create variables by using XSLT elements in the body of the xsl:variable body element. Creating variables this way is sort of like creating XSLT output, but instead of sending the result to the output, the resulting node-set, tree fragment, or simple value is stored in the variable and is usable as long as the variable is in scope. This way, you can create variables depending on some condition or build them from one or more iterations. A simple example of such a variable is shown in Listing 8.12.

LISTING 8.12 Variable Created from XSLT Elements

<xsl:variable name="color">
  <xsl:choose>
    <xsl:when test="@year = '1999'">
<xsl:value-of select="$lightblue" />
    </xsl:when>
    <xsl:when test="@year = '2000'">
<xsl:value-of select="$lightgray" />
    </xsl:when>
    <xsl:otherwise>
<xsl:value-of select="$lightgreen" />
    </xsl:when>
  </xsl:choose>
</xsl:variable>

ANALYSIS

Listing 8.12 yields a variable that contains the value of the variables lightblue, lightgray, or lightgreen. The variable’s value is built from the result of the XSLT fragment. In this case, the value is simple or complex, depending on the value of the variable from which this variable will take its value. You also can create variables consisting of multiple nodes by using other XSLT elements.

A common mistake is to write the code in Listing 8.12 as shown in Listing 8.13.

LISTING 8.13 Erroneous Variable Created from XSLT Elements

<xsl:choose>
  <xsl:when test="@year = '1999'">
    <xsl:variable name="color" select="$lightblue" />
  </xsl:when>
  <xsl:when test="@year = '2000'">
    <xsl:variable name="color" select="$lightgray" />
  </xsl:when>
  <xsl:otherwise>
    <xsl:variable name="color" select="$lightgreen" />
  </xsl:when>
</xsl:choose>

ANALYSIS

Listing 8.13 does not yield the same result as Listing 8.12. As soon as the xsl:choose element is closed, the variable created within its body goes out of scope and is no longer available. This means that the variable is available only inside each choice node. In contrast, the variable created in Listing 8.12 is the result of the entire xsl:choose element and is available to all its following siblings and their descendants, which is probably the result you wanted.

Summary

Today you learned that you can create variables for several purposes. Variables are useful as a means of storing information in a central location so that you don’t have to edit values throughout a stylesheet. Instead, you can edit the values in one place, with the additional advantage that you don’t forget to change any values. Creating such variables is straightforward because you can just hard-code the values into the xsl:variable body.

You also learned that variables can be created with a value that comes from the source XML. You can do so by using a select expression or XSLT elements in the xsl:variable body to construct the value. In the latter case, the value doesn’t necessarily have to map one to one with a fragment of the source XML. Creating variables from the source XML can make your code easier to read because you have handy names for data coming from complex expressions or a constructed value. If you want to use data that will become hard (or impossible) to address because of nesting XSLT structures, using variables might be the only solution.

Tomorrow’s lesson will discuss a different type of variable. These variables also can be used to pass on information from one template to another, or even from outside the stylesheet.

Q&A

Q Can I give a value to a variable both by using the select attribute and a value inside the element?

A No. If you create a variable by using a select attribute, the body of the xsl:variable element must be empty.

Q Can I use a separate source document to store all variable data, just like a CSS?

A Yes. This topic will be covered on Day 14, “Working with Multiple XML Sources.”

Q Can I pass variables on to a template when using xsl:call-templates or xsl:apply-templates?

A No. Another element in XSLT makes this task possible. This topic will be discussed in tomorrow’s lesson.

Q Why would I use variables instead of attribute-sets?

A Attribute-sets are a good alternative for static, hard-coded information common to several elements. Variables can contain dynamic information and can also be used in element values.

Workshop

This workshop tests whether you understand all the concepts you learned today. It is helpful to know and understand the answers before starting tomorrow’s lesson. You can find the answers to the quiz questions and exercises in Appendix A.

Quiz

1. True or False: Variables need to have a unique name within a stylesheet.

2. True or False: A global variable is accessible in an entire stylesheet.

3. How do you create a variable named car with Ford Focus as its value?

4. What is the advantage of using the xsl:variable element body to create a variable from values in the source XML over using the select attribute?

5. Is it possible to create a variable that holds only an attribute with its value?

Exercise

1. Create a stylesheet that displays the cars in Listing 8.9 in a table (similar to Listing 8.3), with rows of different car years colored differently. Years 2001 and later might be colored the same.

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

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