Chapter 7. Using Variables and Parameters

XSLT offers several ways to bind a name to a value so that the value can be later referenced by name any number of times in a stylesheet. The variable element binds a name to an immutable value once it’s been evaluated, while the param element binds a name to a default value, but it’s a value you can change. You can define a default value with param and then pass a new value into the stylesheet or to a template. The with-param element allows you to apply or call a template from another template with a new value for one or more parameters, like a method or function call with arguments.

Tip

Variables in XSLT are limited in what they can do. They are not like variables in programming languages that you can reassign over and over again. Generally, you will define a variable once and then reference it as often as you want. You will also generally change the default value of a parameter just when you pass a value to a stylesheet or template. There are ways around this, but, by and large, that is how you use them. In this way, XSLT variables are more similar to constants in a programming language than to variables.

You can use the variable and param elements globally on the top-level as stylesheet-wide values, or locally within templates. If a variable is global, its scope is the entire stylesheet; if it is local, its scope is restricted to the template where it is defined or passed in. The with-param element may appear only as an immediate child of an apply-templates template (for processing child templates) or of a call-template element (for processing named templates—more on this in Chapter 10). A variable can be of any type—Boolean, node-set, number, string, or result tree fragment.

Tip

In XSLT 2.0, you will also be able to use with-param as a child of the apply-imports element. You can’t do that in XSLT 1.0.

This chapter introduces you to using variables and parameters in stylesheets, both globally and locally. I’ll use the general term variable to refer to the values of variable, param, and with-param in this chapter and throughout the book.

You can read about variables and parameters in Section 11 of the XSLT specification.

Defining Variables and Parameters

Before going any deeper into the subject, there are a few things I’d like to discuss that apply to the variable, param, and with-param elements. To begin with, all three elements have just two attributes:

  1. The required name attribute that binds a name to a value of the variable. This is a QName.

  2. The optional select attribute that can contain an expression that defines the value of the variable.

In addition, all three elements also provide two general ways to define a value:

  1. You can define a value using an expression in the select attribute.

  2. You can also define a value using something called a result tree fragment with a template in the content of the element. As discussed in Chapter 4, a result tree fragment is an XSLT 1.0 datatype that defines a fragment of text or markup.

After you define a variable, you can reference its value by preceding the variable name with a dollar sign, such as in $discount. I’ll cover both ways to define a value and discuss the differences.

Warning

By the way, you can’t use circular definitions when defining a variable. This means that you can’t define a variable by referencing itself.

Defining Default Values for Parameters

Unlike a value defined with the variable element, a value defined with the param element can have a default value that you can change. Nevertheless, a parameter is not required to have an explicit, default value; it can just be empty. Said another way, if a parameter does not have an explicitly defined value, the processor will give it a value of a zero-length string.

When you define a global parameter on the top level, you can pass in a new value when the transformation is performed that replaces the default value using a mechanism provided by the XSLT processor; and when you define a local parameter in a template, you can pass in a new value from another template by using with-param. You will see examples of how this works in the later sections, Section 7.3 for global variables, and Section 7.4 for local variables.

Defining Values with Expressions and Templates

As I mentioned earlier, you can define a variable value with an expression in a select attribute or with a template in element content (as you will soon see section Section 7.1.2.2). However, you cannot define a value using both an expression and a template at the same time. In other words, you cannot use the select attribute together with element content to define a single variable, as they are mutually exclusive.

Using the select attribute to define variables

For example, the following declaration defines a value using an expression in a select attribute:

<xsl:variable name="discount" select="0.40 + 0.30"/>

The expression adds the numbers 0.40 and 0.30 and the resulting number value of 0.70 is bound to the discount variable. An XSLT processor automatically knows that this variable is a number. Likewise, the following variable would be interpreted containing the number 50:

<xsl:variable name="discount" select="50"/>

You can also bind a string to a variable explicitly using embedded quotation marks:

<xsl:variable name="discount" select="'n/a'"/>

Notice the single quotes inside the double quotes in the value of select. This binds the string value n/a to discount. You could also write the declaration in this way, with double quotes inside of single quotes:

<xsl:variable name='discount' select='"n/a"'/>

Either single or double quotes are fine, but you aren’t allowed to mix them (that is, name='discount" is illegal).

Warning

If you enclose the value of select in just double or single quotes, without any internal quotes, the value is interpreted as a node-set and not as a string. Because / is not a legal XML name character, n/a would be interpreted as a location path—the element n with a child a—probably not what you are after.

Because select contains an expression, you can use arithmetic, functions, even references to other variables, when defining a value for a variable with select. Here’s yet another example showing a slightly more complex expression that defines a parameter:

<xsl:param name="discount" select="floor($option)+0.05"/>

You can also specify a location path in select, as in:

<xsl:param name="discount" select="catalog/value"/>

This variable would extract the content of the value element for its value. Another possibility is to use the document( ) function in select like this:

<xsl:param name="discount" select="document('discount.xml')"/>

With this, the value of discount is picked up from the external document discount.xml:

<value>0.10</value>

Though it is discussed elsewhere in the book, you’ll learn more about the document( ) function in Chapter 13.

Using result tree fragments to define variables

When you define a variable using a template in element content, such content is a result tree fragment. Because it is defined as a template, a result tree fragment can be a node-set consisting of markup, which has its own root element. The following declaration uses a result tree fragment to define a variable:

<xsl:variable name="discount">
<xsl:element name="discount">0.10</xsl:element>
</xsl:variable>

Here element is used to create an element named discount with the content 0.10 for the result tree.

The result tree fragment type is defined by XSLT, not by XPath. It is called a temporary tree in XSLT 2.0, and it can be manipulated by an XSLT 2.0 processor in more sophisticated ways than a result tree fragment can be manipulated by an XSLT 1.0 processor. (An XSLT 2.0 temporary tree, however, cannot be manipulated by an XSLT 1.0 processor.) See the section Section 7.5, later in this chapter for a working example.

Using Variables

Variables in XSLT allow you to associate a name with a value, making it easier to use a given value more than once. I’ll start the series of examples in this chapter with a single, global variable that contains a numeric value that is available in every template in the stylesheet, if needed. The following document, price.xml , in examples/ch07, represents a single catalog entry:

<?xml version="1.0"?>
<!DOCTYPE catalog SYSTEM "price.dtd">
   
<catalog>
 <item id="SC-0001">
  <maker>Scratchmore</maker>
  <description>Wool sweater</description>
  <size>L</size>
  <price>120.00</price>
  <currency>USD</currency>
 </item>
</catalog>

price.xml happens to be valid with regard to the DTD price.dtd , also in examples/ch07. The stylesheet variable.xsl derives a discounted price from price.xml and outputs new content for the catalog:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:output doctype-system="catalog.dtd"/>
<xsl:variable name="discount" select="0.10"/>
   
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item"/>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:copy>
  <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
  <xsl:copy-of select="maker|description|size|price"/>
  <discount><xsl:value-of select="$discount"/></discount>
  <discountPrice><xsl:value-of select="price - (price * $discount)"/></discountPrice>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

The stylesheet outputs XML that includes a new document type declaration referencing catalog.dtd (available in examples/ch07). Because the variable element is at the top level, it declares a global variable, discount, that is available or visible to all templates in the stylesheet. This variable is referenced later in the stylesheet with the variable reference $discount. A variable reference is preceded by a dollar sign ($ ).

Tip

There is no internal conflict, by the way, between the element name discount and the variable name discount. There is also no name conflict between a variable defined on the top level and one with the same name defined in a template. However, there will be a name conflict if two or more variables share the same name and are defined on the top level (unless they have a different import precedence; see Chapter 13 for an explanation), or if they share the same name and are defined locally in a template.

The stylesheet makes copies of the maker, description, size, and price elements (maker|description|size|price). In Chapter 4, the | operator was said to imply or. In this instance, the | operator implies and. In other words, the elements maker and description and size and price are all copied. (| can generally be read as union.)

The stylesheet creates two new elements in the result tree, discount and discountPrice. The content of these elements is formed with the aid of the discount variable, which contains a discount percentage of 10 percent (0.10). The discount and discountPrice elements will contain content that is computed with the value of the discount variable.

To see the result, transform price.xml with variable.xsl with the following command:

xalan -v -i 1 price.xml variable.xsl

The -v option instructs Xalan to validate the source document price.xml against the DTD it references in its document type declaration (price.dtd), not the DTD output document references (catalog.dtd). The -i option indicates that child elements should be indented by one space per child. This command will give you the following output:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
 <item id="SC-0001">
  <maker>Scratchmore</maker>
  <description>Wool sweater</description>
  <size>L</size>
  <price>120.00</price>
  <discount>0.1</discount>
            <discountPrice>108</discountPrice>
  <currency>USD</currency>
 </item>
</catalog>

The added discount element reports the discount percentage, and the new discountPrice element contains the calculated discounted price. You can improve the appearance of numbers in the discount and discountPrice elements by using the format-number( ) function in the stylesheet. (format-number( ) was mentioned in Chapter 5 and you will learn more about it in Chapter 9.) variable-alt.xsl uses both the format-number( ) and document( ) functions:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="discount" select="document('discount.xml')"/>
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item"/>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:copy>
  <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
  <xsl:copy-of select="maker|description|size|price"/>
  <discount><xsl:value-of select="$discount"/></discount>
  <discountPrice><xsl:value-of select="format-number(price - 
            (price*$discount),'###.00')"/></discountPrice>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

Xalan reports that it does not support the format-number( ) function, so I’ll use the Instant Saxon processor for this example instead. Instant Saxon 6.5.3, the last version of Instant Saxon, runs on the Windows platform and is available for download from http://saxon.sourceforge.net. (Or just use saxon.exe in examples/ch07). After Instant Saxon is installed, apply variable-alt.xsl to price.xml with this line:

saxon price.xml variable-alt.xsl

You will see a difference from the output of variable.xsl with that of the output of variable-alt.xsl:

<?xml version="1.0" encoding="utf-8"?>
<catalog>
   <item id="SC-0001">
      <maker>Scratchmore</maker>
      <description>Wool sweater</description>
      <size>L</size>
      <price>120.00</price>
      <discount>0.10</discount>
            <discountPrice>108.00</discountPrice>
      <currency>USD</currency>
   </item>
</catalog>

The content of discount was taken from the content of the value element in discount. xml via the document( ) function. Because of this, the content in the output is 0.10 rather than 0.1 (it was 0.1 with variable.xsl). Because of format-number( ), the content of discountPrice is 108.00 rather than just 108. The second argument of format-number( ) (###.00) specifies that the number is to be formatted in the output with two places after the decimal point.

As you know, you can’t change a value defined with variable, so what do you do if you want to change the value of the discount? You can use the param element.

Using Parameters

The following stylesheet, param.xsl , is only a little different than variable.xsl—the top-level element variable is switched with a param element—but what a difference that small change makes:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:output doctype-system="catalog.dtd"/>
<xsl:param name="discount" select="0.10"/>
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item"/>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:copy>
  <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
  <xsl:copy-of select="maker|description|size|price"/>
  <discount><xsl:value-of select="$discount"/></discount>
  <discountPrice><xsl:value-of select="price - (price * discount)"/></discountPrice>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

This stylesheet defines a global parameter (it’s global because it is defined on the top level of the stylesheet). This means that the parameter discount is available throughout the stylesheet, wherever it might be needed. Given this stylesheet, the default value of discount is 0.10, and you can change the value of discount using a mechanism provided by the XSLT processor on the command line.

Tip

This book focuses mostly on XSLT processors that have command-line interfaces. For XSLT processors written in Java and based on Sun’s Java API for XML Processing (JAXP), for example, APIs provide programmatic methods for setting parameter values, namely javax.xml.transform.setParameter(String name, Object value). You will get a chance to use JAXP in a programming example in Chapter 17.

Passing in a Parameter with Xalan

The Xalan processor has a -p command-line option. This option allows you to associate a parameter name with a new value, which is in turn handed to the processor to produce a different result tree. To increase the amount of the discount from 10 percent (0.10) to 20 percent (0.20), enter the following command line:

xalan -i 1 -p discount '0.20' price.xml param.xsl

This transformation will produce the following result:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
 <item id="SC-0001">
  <maker>Scratchmore</maker>
  <description>Wool sweater</description>
  <size>L</size>
  <price>120.00</price>
  <discount>0.20</discount>
               <discountPrice>96</discountPrice>
  <currency>USD</currency>
 </item>
</catalog>

Notice the new content of the discount and discountPrice elements. This was a result of passing in a parameter value. You can use the -p option for as many parameters as you wish to change, provided that those parameters are defined in the stylesheet you are processing. Other processors, such as Instant Saxon, use a different syntax to pass in parameter values.

Passing in a Parameter with Instant Saxon

The Instant Saxon processor uses a simpler command-line syntax than Xalan’s syntax. To associate a parameter name with a new value, increasing the discount from 10 percent (0.10) to 30 percent (0.30), enter this line:

saxon price.xml param.xsl discount="0.3"

The parameter discount is coupled with a new value just by using the equals sign (=). Here is the output:

<?xml version="1.0" encoding="utf-8"?>
   
<!DOCTYPE catalog
  SYSTEM "catalog.dtd">
<catalog>
   <item id="SC-0001">
      <maker>Scratchmore</maker>
      <description>Wool sweater</description>
      <size>L</size>
      <price>120.00</price>
      <discount>0.3</discount>
      <discountPrice>84</discountPrice>
      <currency>USD</currency>
   </item>
</catalog>

Other XSLT processors, such as James Clark’s XT and Microsoft’s MSXSL, also use this simple syntax for associating a parameter with a new value.

So far, you have seen a global variable and parameter. Next you’ll see how to define and use a local one.

Invoking Templates with Parameters

The with-param.xsl stylesheet shown in Example 7-1 doesn’t define a global parameter on the top level, but it does define a local variable within a template.

Example 7-1. A stylesheet using a locally scoped variable
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:output doctype-system="catalog.dtd"/>
   
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item">
   <xsl:with-param name="discount" select="'0.50'"/>
  </xsl:apply-templates>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:param name="discount"/>
 <xsl:copy>
  <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
  <xsl:copy-of select="maker|description|size|price"/>
  <discount><xsl:value-of select="$discount"/></discount>
  <discountPrice><xsl:value-of select="price - (price * $discount)"/></discountPrice>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

The first child element of the template that matches item is the param element, which just happens to be empty by default. When you use local parameters defined with param, the param element must appear as the first child element in a template. This was probably so that their values are taken into account before the template is processed. You can also define local variables in a template with the variable element, but these can appear anywhere in a template. They don’t have to be first in line.

The template that matches catalog applies templates to item elements, but as it does so, it adds a child to apply-templates called with-param. This element passes a new value for the local discount parameter to the template that matches item. To see how it works, enter the following line at a prompt:

xalan -i 1 price.xml with-param.xsl

The processor yields the following deeply discounted results:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
 <item id="SC-0001">
  <maker>Scratchmore</maker>
  <description>Wool sweater</description>
  <size>L</size>
  <price>120.00</price>
  <discount>0.50</discount>
            <discountPrice>60</discountPrice>
  <currency>USD</currency>
 </item>
</catalog>

You can use the with-param element in two places, either as a child of apply-templates or as a child of call-template. You will see how you can use with-param with call-template in Chapter 10.

Tip

In XSLT 1.0, parameters are not passed through by built-in templates. XSLT 2.0, however, does pass through parameters via built-in templates.

In the final examples in this chapter, I’ll show you how to create a variable value with a result tree fragment, and then how to use that fragment later in the stylesheet.

Using Result Tree Fragments

As you saw earlier, a variable value, created with a template as element content, creates a result tree fragment. The stylesheet in Example 7-2, fragment.xsl , constructs a variable value for discount using a template with a variable element, and then later accesses that value with copy-of.

Example 7-2. Copying a variable
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:output doctype-system="catalog.dtd"/>
<xsl:variable name="discount">
               <discount>0.40</discount>
               <discountPrice><xsl:value-of select="format-number(catalog/item/price *
0.60, '###.00')"/></discountPrice>
               </xsl:variable>
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item"/>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:copy>
  <xsl:copy-of select="@id"/>
  <xsl:copy-of select="maker|description|size|price"/>
  <xsl:copy-of select="$discount"/>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

Notice the value-of contained in discountPrice in variable. The first argument of format-number( ) uses a location path to address the content of the price element in the source tree. The context node for evaluating a global variable is the root node of the original source document; that’s why the location path uses catalog/item/price instead of /catalog/item/price, an absolute location path.

Later, in the template for item, copy-of elements copy the id attribute from the source item and the nodes contained in discount’s result tree fragment.

To test it out, enter this command line:

saxon price.xml fragment.xsl

You will get this outcome:

<?xml version="1.0" encoding="utf-8"?>
   
<!DOCTYPE catalog
  SYSTEM "catalog.dtd">
<catalog>
   <item id="SC-0001">
      <maker>Scratchmore</maker>
      <description>Wool sweater</description>
      <size>L</size>
      <price>120.00</price>
      <discount>0.40</discount>
      <discountPrice>72.00</discountPrice>
      <currency>USD</currency>
   </item>
</catalog>

A result tree fragment does not have to be well-formed XML. For example, you could also just use a bit of text in a result tree fragment, as shown in the variable definition for discount in Example 7-3, frag.xsl.

Example 7-3. Using text as a result tree fragment
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:output doctype-system="catalog.dtd"/>
<xsl:variable name="discount">0.70</xsl:variable>
<xsl:variable name="discountPrice" select="format-number(catalog/item/price - 
(catalog/item/price) * $discount, '###.00')"/>
   
<xsl:template match="catalog">
 <xsl:copy>
  <xsl:apply-templates select="item"/>
 </xsl:copy>
</xsl:template>
   
<xsl:template match="item">
 <xsl:copy>
  <xsl:copy-of select="@id"/>
  <xsl:copy-of select="maker|description|size|price"/>
  <discount><xsl:value-of select="$discount"/></discount>
  <discountPrice><xsl:value-of select="$discountPrice"/></discountPrice>
  <xsl:copy-of select="currency"/>
 </xsl:copy>
</xsl:template>
   
</xsl:stylesheet>

The variable discount contains only the text 0.70. (It’s not best to define a simple value like 0.70 as a tree fragment, as it is somewhat more costly processing-wise to do so.) When the discountPrice variable is defined, it contains a reference to discount as part of its definition. The two variables are referenced later in the stylesheet by instances of value-of.

Tip

You can reference a global variable before you define it, but you must define a local variable before you reference it.

Watch what happens with this command:

xalan -i 1 price.xml frag.xsl

Here is the result:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE catalog SYSTEM "catalog.dtd">
<catalog>
 <item id="SC-0001">
  <maker>Scratchmore</maker>
  <description>Wool sweater</description>
  <size>L</size>
  <price>120.00</price>
  <discount>0.70</discount>
  <discountPrice>36.00</discountPrice>
  <currency>USD</currency>
 </item>
</catalog>

Before concluding, I should mention a few more things about result tree fragments. Only XSLT 1.0 allows you to copy result tree fragments or use them as strings. However, the node-set( ) extension function offered by many XSLT 1.0 processors allows you to use a result tree fragment as a tree of temporary nodes. node-set( ) is not actually part of XSLT 1.0, but it is a useful extension added by many XSLT processors. You will learn how to use two versions of node-set( ) in Chapter 15. XSLT 2.0 uses temporary trees rather than result tree fragments, which are stricter. Unlike its 1.0 predecessor, XSLT 2.0 is a tree of XML nodes and cannot contain mere fragments of text.

Summary

In this chapter, you have learned how to define variables and parameters using variable and param. You have also learned how to pass parameters into a stylesheet using XSLT processor mechanisms and to pass them within a stylesheet using with-param. Result tree fragments now should be a little less mysterious. You will have other encounters with variables and parameters in the chapters that follow.

In the next chapter, you’ll learn how to sort nodes with the XSLT sort instruction.

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

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