Chapter 8. Working with Variables

CONTENTS
  •  8.1 Declaring and Binding Variables
  •  8.2 Result Tree Fragments
  •  8.3 Using Variable References
  •  8.4 Comparing <xsl:variable> and <xsl:param>
  •  8.5 Comparing <xsl:with-param> to <xsl:param> and <xsl:variable>
  • <xsl:variable>

  • <xsl:param>

  • <xsl:with-param>

XSLT provides the functionality to create special values that can be declared and used by expressions in other elements in the stylesheet. These values are attached, or bound, to named objects called variables. A variable in XSLT can be either global (available throughout the stylesheet) or local (available only to the element where it is declared), and it must be declared before it can be used.

In this chapter, we will discuss the two ways to declare variables in XSLT, using either <xsl:variable> or <xsl:param>, and the process of calling, or referencing, variable values. We will also discuss <xsl:with-param>, which allows a template to override the value of a variable that was previously declared using an <xsl:param> element.

8.1 Declaring and Binding Variables

Variables are declared in an XSLT stylesheet with either the <xsl:variable> element or the <xsl:param> element. Binding a value to a variable is the process of assigning the variable to a value. Therefore, when a value is assigned to a variable, it is said to be bound to that variable. Declaring a variable and binding a value to a variable happen simultaneously. It is not possible to declare a variable without also assigning a value to it. It may help to remember that, when a variable is declared, it refers to the physical location of the <xsl:variable> or <xsl:param> in the stylesheet, and when a variable is bound, it refers to the processor's instantiation of the value of that variable.

Variable values are bound at the point in the stylesheet where they are declared, not where they are called. This means that any evaluation of an expression in a variable uses the context of the variable declaration as its context. In other words, where the variable is declared is very important because the value of the variable is tied to the current context.

Global variables declared with <xsl:variable> are not variable in the true sense of the word. They are bound to a value using the root node as the context, and they keep their assigned value throughout the processing of the stylesheet. Once a global variable that is declared with <xsl:variable> is bound to a value, that variable will remain the same. However, it is possible to declare a local variable of the same name to temporarily override that global variable value.

Global variables that are declared using the <xsl:param> element are variable because they have an initial default value that can be overridden with the <xsl:with-param> element during the processing of a template.

Local variables (declared with either <xsl:variable> or <xsl:param>) are also variable because they are reinstantiated; that is, their values are reassigned according to the current context during the processing of a template.

8.1.1 The <xsl:variable> Top-Level Element

The <xsl:variable> element is used to declare a variable with a fixed value, and can function both as a top-level element and as an instruction element. When declared as a top-level element, the variable is considered to be global. When declared as an instruction element, it is considered to be local. The <xsl:variable> element accepts two attributes: the name attribute, which is required, and the select attribute, which is optional. The following element model definition shows the content of the <xsl:variable> element as a template, but the content is not allowed if the select attribute contains an expression:

<!-- Category: top-level-element --> <!-- Category: instruction --> <xsl:variable   name = qname   select = expression>   <!-- Content: template --> </xsl:variable> 

The required name attribute of the <xsl:variable> element is used to identify the variable and takes the form of a QName. A variable reference uses the same name to retrieve the value of the variable (see Section 8.3 for more information on variable references).

The optional select attribute contains an expression and is used to bind the value of the variable to the object that is the result of evaluating the expression. If the select attribute is used, the content of the <xsl:variable> element must be empty.

If there is no select attribute, the content of the <xsl:variable> element is a template, and the value bound to the variable is the result of instantiating that template. Note that the result of instantiating a template in an <xsl:variable> element is a result tree fragment, or RTF (see Section 8.2 for more information on result tree fragments).

The value bound to a variable is available to the expressions in other elements based on where it is declared. When declared within an <xsl:template> element at the instruction element level, it is a variable local to that context only, and it is only available to the functions and elements that follow it, within that template. In other words, the value of that variable is only valid inside the realm of the template where it is declared, and is only available to elements that occur after its declaration. On the other hand, if a variable is declared with an <xsl:variable> as a top-level element, the value of the variable is available globally to the remaining elements in the stylesheet following its declaration.

8.1.2 The <xsl:param> Top-Level Element

The <xsl:param> element is used to declare a variable with a default value. It accepts two attributes: the name attribute, which is required, and the select attribute, which is optional. The content of the <xsl:param> element is a template, but the content is not allowed if the select attribute contains an expression.

The value of the variable declared using the <xsl:param> element is only a default value, which is used unless another explicit value is supplied before the variable is referenced, or called. This default value can be modified prior to use by the <xsl:with-param> element, which is discussed in the following section.

Although not explicitly categorized as an instruction element in the element model definition shown below, the <xsl:param> element is allowed at the same level as instruction elements.[1] However, when used inside a template, it must occur before any other elements in the template. When declared as a top-level element, the variable is considered to be global; when declared at the instruction level, the variable is considered to be local.

<!-- Category: top-level-element --> <xsl:param   name = qname   select = expression>   <!-- Content: template --> </xsl:param> 

The required name attribute of the <xsl:param> element is used to identify the variable and takes the form of a QName. A variable reference uses the same name to retrieve the value of the variable (see Section 8.3).

The optional select attribute contains an expression and is used to bind the value of the variable to the object that is the result of evaluating the expression. If the select attribute is used, the content of the <xsl:param> element must be empty.

If there is no select attribute, the content of the <xsl:param> element is a template, and the value bound to the variable is the result of instantiating that template. Note that the result of instantiating the template in an <xsl:param> element is an RTF (see Section 8.2).

The value bound to the variable is available to the expressions in other elements based on where it is declared. When declared within an <xsl:param> element at the instruction element level, it is a variable local to that context only, and only available to the functions and elements that follow it. In other words, the value of that variable is only valid inside the realm of the template where it is declared, and is only available to elements that occur after its declaration. On the other hand, if the variable is declared with an <xsl:param> as a top-level element, the value of the variable is available globally to the remaining elements in the stylesheet following its declaration.

8.1.3 The <xsl:with-param> Element

The <xsl:with-param> element is used to override the value of a variable declared with <xsl:param>. It has the same basic model, shown in the element model definition below, as <xsl:variable> and <xsl:param>. However, a variable must be declared using <xsl:param> prior to using <xsl:with-param> to override it.

<xsl:with-param   name = qname   select = expression>   <!-- Content: template --> </xsl:with-param> 

The required name attribute of the <xsl:with-param> element is used to identify the variable and takes the form of a QName. A variable reference uses the same name to retrieve the value of the variable (see Section 8.3). Note that a variable with the same name must have been declared using <xsl:param> prior to using this element.

The optional select attribute contains an expression and is used to bind the value of the variable to the object that is the result of evaluating the expression. If the select attribute is used, the content of the <xsl:with-param> element must be empty.

If there is no select attribute, the content of the <xsl:with-param> element is a template, and the value bound to the variable is the result of instantiating that template. Note that the result of instantiating the template in an <xsl:with-param> element is an RTF (see Section 8.2).

Using <xsl:with-param> makes the value of a parameter more flexible. After you have declared a variable with a default value using <xsl:param>, you can use the <xsl:with-param> element to reset that value within the current template's context. In other words, <xsl:with-param> gives a context-specific value for the variable, which affects only the template being called.

The <xsl:with-param> element is neither a top-level element nor an instruction element. It can only be used within the <xsl:call-template> element or the <xsl:apply-templates> elements. The template called or applied using either <xsl:call-template> or <xsl:apply-templates> is therefore the only context that uses the new value of the variable declared with <xsl:with-param>. The original value for the variable declared with <xsl:param> is overridden for the context of the template being called. It is important to note that <xsl:with-param> can only be used to affect the values of variables already declared using <xsl:param>. If there is no declaration of the original variable in place for a template, it is ignored.

8.2 Result Tree Fragments

When a variable declaration element contains a template, the content of the template is instantiated and the resulting object is an RTF. This creates a new kind of object, different from the four standard object types (node-set, string, Boolean, and number). An RTF is a node-set that contains one node, whose children are the nodes that are produced by the instruction elements in the template.

Although an RTF is technically a node-set, there are restrictions on how this new type of node-set can be accessed or used. The way in which the variable is referenced in subsequent template rules determines how that RTF will be written to the actual output result tree. If the RTF is output using <xsl:copy-of>, the nodes in the RTF are sent to the output, including the tags and text content. Otherwise, the output will be the text content of the RTF, without any markup.

When a variable is bound to an RTF, a portion of the actual output result tree is being bound. Additional processing on RTFs resulting from variables is allowed, but not all functions are available. Only functions that can be applied to strings are allowed on RTFs. For example, using count() on an RTF will return an error. Operations that involve patterns and predicates are not allowed. Also, an RTF cannot be further processed using any /, //, or [] tokens in it in other words, no absolute location path tokens, no descendant axis abbreviations, and no predicates. Otherwise, operations performed of the type permitted on strings, when performed on RTFs, behave just as they would on any other equivalent node-set.

Example 8-1 shows a variable that contains a template with three LREs, each containing an instruction element.

Example 8-1 Variable declaration with template content.
<xsl:variable name="numvar">       <num><xsl:value-of select="1"/></num>       <num><xsl:value-of select="2"/></num>       <num><xsl:value-of select="3"/></num> </xsl:variable> 

The result of instantiating the variable is a node-set containing the three <num> nodes. Using <xsl:copy-of select="$numvar"> returns the nodes and their content:

<num>1</num> <num>2</num> <num>3</num> 

The value of the node-set, when extracted using <xsl:value-of select="$numvar">, is the string value of a concatenation of its children; in this case, the string is "123." However, using a node-set function like <xsl:value-of select="count($numvar)"> is an error because node-set operations on RTFs are not allowed.

In an example using Markup City, it is possible to store nodes from the input in a variable as follows:

<xsl:variable name="blocks"> <xsl:copy-of select="//block"/> </xsl:variable> 

This example stores all the <block> elements and their text children in the variable as a single node-set. The value of the new node-set can then be extracted using <xsl:value-of select="$blocks">, which returns the concatenation of the text in each block.

Using <xsl:copy-of select="$blocks"> retrieves the entire set of <block> nodes, including their children text nodes, as shown in Example 8-2.

Example 8-2 Extracting a node-set in a variable using <xsl:copy-of>.
INPUT: <?xml version="1.0"?> <main>       <parkway>             <thoroughfare>Governor Drive</thoroughfare>             <thoroughfare name="Whitesburg Drive">                   <sidestreet>Bob Wallace Avenue</sidestreet>                   <block>1st Street</block>                   <block>2nd Street</block> <block>3rd Street</block>                   <sidestreet>Woodridge Street</sidestreet>             </thoroughfare>             <thoroughfare name="Bankhead">                   <sidestreet>Tollgate Road</sidestreet>                   <block>First Street</block>                   <block>Second Street</block>                   <block>Third Street</block>                   <sidestreet>Oak Drive</sidestreet>             </thoroughfare>       </parkway>       <boulevard>                   <block>Panorama Street</block>                   <block>Highland Plaza</block>       <block>Hutchens Avenue</block>                   <block>Wildwood Drive</block>                   <block>Old Chimney Road</block>                   <block>Carrol Circle</block>      </boulevard> </main> STYLESHEET: <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"              version="1.0"> <xsl:variable name="blocks"> <xsl:copy-of select="//block"/> </xsl:variable> <xsl:template match="/"> <xsl:copy-of select="$blocks"/> </xsl:template> </xsl:stylesheet> RESULT: <?xml version="1.0" encoding="utf-8"?> <block>1st Street</block> <block>2nd Street</block> <block>3rd Street</block> <block>First Street</block> <block>Second Street</block> <block>Third Street</block> <block>Panorama Street</block> <block>Highland Plaza</block> <block>Hutchens Avenue</block> <block>Wildwood Drive</block> <block>Old Chimney Road</block> <block>Carrol Circle</block> 

The template inside the <xsl:variable> element shown in this example is retrieved when the variable is referenced, or called, using the variable reference, $blocks.

8.3 Using Variable References

Regardless of how a variable is declared, either with <xsl:variable> or <xsl:param>, or if it is overridden with <xsl:with-param>, the value bound to the variable is accessed using a variable reference.[2] A variable reference is the mechanism used to retrieve the value of a variable. Variable reference takes the form $variable-name, where "variable-name" is the name of the variable declared using the name attribute on the declaring element. Note that this means the variable name is also by definition a QName.

The template rule in Example 8-3 declares a variable named myparent and gets its value using the name() function. The value of the variable is output with the $myparent variable reference in the <xsl:value-of> element.

Example 8-3 Declaration of a variable with <xsl:variable>.
<xsl:template match="block"> <xsl:variable name="myparent" select="name(..)" /> <xsl:value-of select="$myparent"/> </xsl:template> 

The result of this template is to output the name of the parent for each <block> element. Variable references can only be used in the context of expressions. However, you cannot use a variable reference directly in a match or use attribute. Some processors do allow variable references inside the predicate of an expression within the match or use attribute. For example, in XT, it is not valid to use $num in a match for the template rule <xsl:template match="$num"/>, but it is valid in the predicate of match for the template rule <xsl:template match="//*[$num]"/>. If the value of $num is 3, the template rule will match every element that has a position of 3. While this functionality is available with some processors, it is not guaranteed to work with all or future versions of processors.

8.3.1 Local vs. Global Variables

The value of a variable is bound to the variable within the context of the declaration. In other words, the value is determined based on where the variable is declared. The value is available to the elements that occur after the declaration has been made, but only within the same context as the declaration. However, if the variable is declared using a top-level element, it is considered a global variable and its value is bound based on the root node as a context. Global variables are available to all elements and their children, regardless of where reference to the variable occurs in the stylesheet.

If a variable is declared within the context of a template rule, that variable is a local variable and is only available to the contents of that template rule. Local variables do not pass on their value to any templates called by the template rule with <xsl:apply-templates>, <xsl:call-template>, or <xsl:apply-imports>. However, variable values can be passed to templates using the <xsl:with-param> element in either <xsl:apply-templates> or <xsl:call-template>.

Example 8-4 shows a template matching on <parkway> that contains a local variable. An additional template matching on <block> tries to use the same variable, but cannot, because the value of the local variable is not defined in the context of <block>.

Example 8-4 Using local variables incorrectly.
<?xml version="1.0"?> <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="parkway"> <xsl:variable name="num" select="3" /> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="block"> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet> 

Using this example would produce an error because the variable num is declared as a local variable in the context of <parkway>. The template matching on block cannot "see" the value of the variable. Declaring the variable at the top level as a global variable would resolve this problem, as shown in Example 8-5.

Example 8-5 Using global variables.
<?xml version="1.0"?> <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:variable name="num" select="3" /> <xsl:template match="parkway"> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="block"> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet> 

8.3.2 Duplicate Declarations (Shadowing)

When a value is bound to a variable, the value of the variable becomes static, or fixed, within the context of its binding. The value of a variable can only be changed after it has been bound by rebinding the value with a new declaration or, if it was originally declared using the <xsl:param> element, by using the <xsl:with-param> element to pass a new variable value in its place. Declaring a variable that is a duplicate of a previously declared variable is called shadowing.

According to the XSLT specification, shadowing at the same level by declaring two variables with the same name is not allowed. If this occurs, the results vary depending on the processor used. Some processors may return an error, while others may use the last variable declared. Declaring variables with the same name in different contexts, however, is allowed.

Declaring a local variable with the same name as one that was previously declared at the top level is valid. A local variable with the same name as a global variable temporarily overrides the value of the global variable within the context of the local declaration of the variable. Example 8-6 demonstrates the order of precedence for variables with duplicate names.

Example 8-6 Order of precedence for variables with duplicate names.
<?xml version="1.0"?> <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:variable name="num" select="1" /> <xsl:param name="num" select="2" /> <xsl:template match="parkway"> <xsl:param name="num" select="3" /> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> <xsl:template match="block"> <xsl:value-of select="$num"/> <xsl:apply-templates/> </xsl:template> </xsl:stylesheet> 

In this stylesheet, the variable named num is defined three times, two at the top, or the global, level and once locally within the context of the <parkway> element. The resulting value of the variable is 2 in the global sense, but 3 when it is redeclared as a local attribute inside the template rule for <parkway>.

Note

Some processors will simply ignore the first <xsl:variable> declaration (the one with a value of 1). Other processors will generate an error when any shadowing in the same context occurs.

Variable values declared locally in a template are not inherited by the template rules for the children of the element. So, the template rule for <block> would use the global value of 2, not the local value of 3.

Using our Markup City, Example 8-7 shows a variable declaration that returns a different value based on which element is the parent of the current block.

Example 8-7 Context based <xsl:variable> declaration.
STYLESHEET: <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="text()"/> <xsl:template match="block"> <xsl:variable name="myparent" select="name(..)" /> <xsl:value-of select="$myparent"/><xsl:text>: </xsl:text> <xsl:value-of select="text()"/> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> RESULT: thoroughfare: 1st Street thoroughfare: 2nd Street thoroughfare: 3rd Street thoroughfare: First Street thoroughfare: Second Street thoroughfare: Third Street boulevard: Panorama Street boulevard: Highland Plaza boulevard: Hutchens Avenue boulevard: Wildwood Drive boulevard: Old Chimney Road boulevard: Carrol Circle 

Based on the context, the value of the variable myparent is different depending on which <block> is being processed. Using the name() function with two dots .. (the abbreviation for the parent) in the variable declaration <xsl:variable name="myparent" select="name(..)" /> binds the name of the parent of the context node to the variable. The variable is referenced using <xsl:value-of select="$myparent"/>. A little creative use of <xsl:text> puts in line breaks and the colon, and <xsl:value-of select="text()"/> pulls out the text nodes in the blocks (notice that the first template rule, <xsl:template match="text()"/>, is used to suppress all the text nodes initially). If the context node is a <block> in the <thoroughfare>, the value of the variable is thoroughfare. If the context node is a <block> in the <boulevard>, the value of the variable is boulevard. The local variable myparent is being reinstantiated for each <block> element that it encounters.

8.3.3 Using the <xsl:with-param> Instruction Element

The <xsl:with-param> element can be used to change the value of a variable used in a template rule by passing a new value for that variable from another template rule. The variable that is being passed in must have been previously declared in the context of the template being called, or the new variable will just be ignored. This allows parent templates to pass values into templates for children elements. Recall that variables are limited to the scope of the context where they are declared, so simply declaring the variable in the parent template of an element does not make that variable available to the child element's template.

For example, suppose we wanted to divide up our city by precinct based on the kind of street, either a thoroughfare or a boulevard. We could change the value of the "precinct" variable defined in the <block> template using an <xsl:with-param> element inside an <xsl:apply-templates> element, as shown below.

<xsl:template match="thoroughfare">       <xsl:apply-templates select="block">             <xsl:with-param name="precinct" select="5"/>       </xsl:apply-templates> </xsl:template> <xsl:template match="block">       <xsl:param name="precinct" select="4"/>       <xsl:value-of select="$precinct"/> </xsl:template> 

In this case, the <block>s that are in <thoroughfare>s would use the "precinct" value 5, but the <block>s in any other context would use the default value declared in the <block> template rule, 4. Normally, templates would not be able to pass the value of a variable to other templates, but <xsl:with-param> allows this to happen. Example 8-8 shows a complete stylesheet.

8.4 Comparing <xsl:variable> and <xsl:param>

Syntactically, there is no difference between <xsl:variable> and <xsl:param>. The content model of both elements is the same. The way these elements declare variables is the same. However, the value of the variable declared using <xsl:param> is only a default that can be changed with the <xsl:with-param> element, while the <xsl:variable> value cannot be changed. The only other difference is that there is a restriction on where the <xsl:param> element can be used. It must always come before any other elements if used within a template rule.

Because <xsl:variable> and <xsl:param> declare variables in the same way, if they are both present in the same context and they both have the same name, they are considered duplicates and result in an error. Duplicate declarations, or shadowing, are discussed in Section 8.3.2.

Using <xsl:param> together with <xsl:variable> raises certain contextual restrictions on syntax. When a variable and parameter are both declared in a template, the <xsl:param> must come first.

8.5 Comparing <xsl:with-param> to <xsl:param> and <xsl:variable>

The <xsl:with-param> element has the same content model and declares a variable in the same manner as <xsl:variable> and <xsl:param>. However, <xsl:with-param> must be used inside either <xsl:apply-templates> or <xsl:call-template>, and using it replaces the value of the variable that was declared using the original <xsl:param>. If there is no <xsl:param> declaration in the template being called, the variable is ignored. The <xsl:with-param> element cannot be used to change the value of a variable declared with <xsl:variable>.

Example 8-8 Extended example using <xsl:param> and <xsl:with-param>.
STYLESHEET: <xsl:stylesheet       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"       version="1.0"> <xsl:template match="text()"/> <xsl:template match="thoroughfare"> <xsl:apply-templates select="block"> <xsl:with-param name="precinct" select="5"/> </xsl:apply-templates> </xsl:template> <xsl:template match="block"> <xsl:param name="precinct" select="4"/> Precinct: <xsl:value-of select="$precinct"/> Block name: <xsl:value-of select="text()"/> </xsl:template> </xsl:stylesheet> OUTPUT: Precinct: 5 Block name: 1st Street Precinct: 5 Block name: 2nd Street Precinct: 5 Block name: 3rd Street Precinct: 5 Block name: First Street Precinct: 5 Block name: Second Street Precinct: 5 Block name: Third Street Precinct: 4 Block name: Panorama Street Precinct: 4 Block name: Highland Plaza Precinct: 4 Block name: Hutchens Avenue Precinct: 4 Block name: Wildwood Drive Precinct: 4 Block name: Old Chimney Road Precinct: 4 Block name: Carrol Circle 

[1] Technically, <xsl:param> is not a true "instruction element" even though it is allowed at the same level as instruction elements. Instruction elements are executed and replaced by the RTFs that they create, while an <xsl:param> element only defines a default value.

[2] Although variables are defined in XSLT, variable references are the realm of XPath because they are used in expressions.

CONTENTS


XSLT and XPATH(c) A Guide to XML Transformations
XSLT and XPATH: A Guide to XML Transformations
ISBN: 0130404462
EAN: 2147483647
Year: 2005
Pages: 18

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