Functions, Variables, and Imports


When developing complex XSLT applications, writing all the XSLT instructions in one document becomes very tedious. It’s more useful to break the XSLT into separate function calls, which can either be defined in the same document or in another document. By breaking the XSLT into smaller chunks, it is simpler to maintain and develop the code. The following XML code is used as a basis for all the XSLT examples in this chapter:

<data>     <child>         <elements index=”1”>Hello</elements>     </child>     <elements index=”2”>         <sub>             <elements index=”3”> Embedded</elements>         </sub>     </elements> </data>

Creating variables

When you make an XSLT function call, the state of the specific value needs to be stored somewhere temporarily. In XSLT, as in other programming languages, that somewhere is in a variable. Unlike in other programming languages, however, variables in XSLT are read-only. This means that after a value is assigned to an XSLT variable, it can never be changed. Probably, at this point, many readers are thinking sarcastically, “Which genius came up with that brilliant idea?” The answer is that it is necessary to make the variables read-only so that the XSLT transformation process doesn’t become inconsistent. Consider the following number sequence:

1,5,6,89,91

Assume that a query executes, selects the number 6 and 91, and stores them in a variable. Then another query executes on the variable and selects the number 6. Finally, at some point in time, the original query executes a change and sets the numbers to 5 and 89. What happens to the second query? Does it execute again to build another node set? It can be argued that specific rules could be introduced to handle this situation. But XSLT is a transformation language that changes data from one form to another. Even in SQL, where data is transformed and manipulated in a way similar to XSLT, the selected data sets are typically static data sets. Once selected, the value never changes (unless you requery the SQL data). The XSLT group decided to keep things simple and say that data is read-only once assigned.

To create a variable, you use the xsl:variable instruction as shown in the following example:

<xsl:stylesheet version=”1.0” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:template match=”elements”>         <xsl:apply-templates />         <xsl:variable name=”var1” select=”text()” />         <xsl:value-of select=”$var1” />     </xsl:template>     <xsl:template match=”text()”>     </xsl:template> </xsl:stylesheet>

The xsl:variable instruction has two attributes: name and select. The name attribute is the identifier of the variable, which in this case is var1, a case-sensitive variable. The select attribute selects the data that is to be assigned to the variable. The selection could be any XPath that results in a simple text or a node set select. After the variable has been assigned, it is possible to use it by referencing the variable using the special dollar character prepended to the variable name (var1), as illustrated by the xsl:value-of instruction. Note that the select attribute in the xsl:variable could have been directly inserted into the xsl:value-of select attribute. We did not use that method because it would not have shown you how to use a variable.

It is possible to assign a variable using literal values as in the following example:

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

The variable var1 has a value of 2. Another example is as follows:

<xsl:variable name=”var1” select=”Genf” />

In this case, you might assume that the variable var1 has been assigned the string Genf. That assumption is incorrect. The variable var1 has been assigned the Genf XML node. To assign a string variable to variable var1, the following xsl:variable instruction has to be used:

<xsl:variable name=”var1” select=”’Genf’” />

Embedding variable declaration

The xsl:variable select is optional because, unlike the xsl:value-of instruction, the selection can be a combination of multiple data items, as shown in the following example:

<xsl:stylesheet version=”1.0”  xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:template match=”elements”>         <xsl:apply-templates />         <xsl:variable name=”var1” >              My value is (<xsl:value-of select=”text()” />)          </xsl:variable>          <xsl:value-of select=”$var1” />     </xsl:template>     <xsl:template match=”text()”>     </xsl:template> </xsl:stylesheet>

The xsl:variable instruction still has the name attribute, but the value of the var1 variable is based on the instructions contained. In this example, the value of the variable is the text My Value is concatenated with the value of the xsl:value-of instruction. A variable can be the result of a loop, template call, or whatever else is legal in XSLT.

Embedding values using this technique could be useful when you are building literal strings manually. There is a catch, however, because embedding variables means creating node sets and not literal values, as shown in the following example:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/ XSL/Transform">     <xsl:template match="elements">         <xsl:apply-templates />         <xsl:variable name="temp">1</xsl:variable>         <xsl:variable name="var1" >              My value is (<xsl:value-of select="parent::*" />)          </xsl:variable>          (<xsl:value-of select="text()" />)          (<xsl:value-of select="@index" />)          (<xsl:value-of select="child::*[$temp]" />)     </xsl:template>     <xsl:template match="text()">     </xsl:template> </xsl:stylesheet> 

The purpose of this XSLT sheet is to output a specific child element, whenever an element is found. In this case, the child element index is in the variable $temp. Executing this XSLT sheet on the original XML document results in the following:

<?xml version=”1.0” encoding=”UTF-8”?>          (Hello)          (1)          ()          (Embedded)          (3)          ()          ()          (2)          (Embedded)

The result is correct because only the elements XML node with an index of 2 has a child with index 1. If you try out another index value, such as the second child index, you need to make the following XSLT changes:

<xsl:apply-templates /> <xsl:variable name=”temp”>2</xsl:variable> <xsl:variable name=”var1” >      My value is (<xsl:value-of select=”parent::*” />)

Executing the modified XSLT document gives the following result:

<?xml version="1.0" encoding="UTF-8"?>          (Hello)          (1)          ()               (Embedded)          (3)          ()               ()          (2)          (Embedded) 

This is a puzzling result because the index modification changed nothing. The answer to the puzzle is that the $temp variable is not a literal value, but a node set value. To retrieve the correct position, the XSLT code has to be modified to the following:

     (<xsl:value-of select=”@index” />)      (<xsl:value-of select=”child::*[position() = $temp]” />) </xsl:template>

Running the modified XSLT document gives the following result:

<?xml version=”1.0” encoding=”UTF-8”?>          (Hello)          (1)          ()               (Embedded)          (3)          ()               ()          (2)          ()

This is the correct result because there is no second child XML node below any of the elements XML nodes. The reason this modification works is because the $temp variable is converted to a data type that is identical to the function position(), which is a numeric.

How Variables are Scoped

In the previous examples, all of the variables declared were at the xsl:template scope level. But there is a catch. The scope of variables in a contained XSLT statement changes how a variable can be referenced. Consider the scope change of the variable var2 in the following example:

<xsl:template match="elements">     <xsl:apply-templates />     <xsl:variable name="var1" >         <xsl:variable name="var2" select="text()" />          My value is (<xsl:value-of select="parent::*" />)      </xsl:variable>      <xsl:value-of select="$var1" />      <xsl:value-of select="$var2" /> </xsl:template> 

In this example, the variable var2 is declared within the declaration of var1. Outside of the variable var1, declaration var2 is not visible. The xsl:value-of that references the var2 variable generates an exception because the variable is not declared.

To declare a variable at the global-scope level, you add xsl:variable after the xsl:stylesheet instruction, as shown in the following example:

<xsl:stylesheet version=”1.0”  xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:variable name=”globalVariable” select=”/” />     <xsl:template match=”elements”>     </xsl:template>     <xsl:template match=”text()”>     </xsl:template> </xsl:stylesheet>

The variable globalVariable is visible to the templates elements and text(). The only requirement with the variable globalVariable is that the XPath or embedded variable declaration needs to be absolute. The XPath cannot resolve to an element that will be matched like the xsl:template instruction.

Global variables can be hidden from a particular scope if a local variable with the same name is declared, as in the following example:

<xsl:stylesheet version=”1.0”  xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>     <xsl:variable name=”globalVariable” select=”/” />     <xsl:template match=”elements”>         <xsl:variable name=”globalVariable” select=”//elements” />     </xsl:template>     <xsl:template match=”text()”>     </xsl:template> </xsl:stylesheet>

Applying templates

Thus far, the templates presented have had an embedded xsl:apply-templates to process all the subnodes. The xsl:apply-templates instruction, as presented, processes all child nodes. It is possible to either filter or pass on extra data, as shown in the following example:

<xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">     <xsl:template match="sub">        <xsl:param name="functionName" />          [<xsl:value-of select="$functionName" />]     </xsl:template>     <xsl:template match="elements">         <xsl:apply-templates>             <xsl:with-param name="functionName" select="’Hello’"/>         </xsl:apply-templates>     </xsl:template>     <xsl:template match="text()">     </xsl:template> </xsl:stylesheet> 

This example includes an additional xsl:template instruction that matches the XML subnode. Directly after the xsl:template instruction declaration is the xsl:param instruction. The xsl:param instruction is used to declare a parameter. It is like the xsl:variable parameter, except that the value is bound to the xsl:template instruction.

This is a change that requires a higher-level xsl:template match to create a variable. The variable is created by the xsl:template match of the elements XML node. Within that template is an xsl:apply-templates instruction with an embedded xsl:with-param instruction. The xsl:with-param instruction declares a parameter that is to be passed on whenever the xsl:apply-templates matches an XML node. In this example, all child nodes of the current context will have an additional parameter. The xsl:with-param instruction has two attributes: name and select. The name attribute identifies the parameter to define, which must be the same as the associated xsl:param instruction of the xsl:template instruction. The select attribute functions similarly to the way it was used in other XSLT instructions. It is possible to embed content within the xslt:with-param instruction similar to the way it is embedded in the xsl:variable instruction.

Executing the XSLT document on the defined XML document results in the following output:

<?xml version=”1.0” encoding=”UTF-8”?>          [Hello]

This is the output that we are expecting.

The xsl:apply-templates instruction can also be used to filter which child objects get executed, as the following example shows:

<xsl:apply-templates select="sub">     <xsl:with-param name="functionName" select="’Hello’"/> </xsl:apply-templates> 

In this example, the sub-elements have been filtered to execute only the XML subelements. If this were to execute on the example XML document, you would generate the same result as you got from the previous XSLT.

The other XSLT instruction that can be embedded within the xsl:apply- templates instruction is the xsl:sort instruction.

Cross-Reference See Chapter 6 for details about the xsl:sort instruction.




The XMLSPY Handbook
The Official XMLSPY Handbook
ISBN: 764549642
EAN: 2147483647
Year: 2001
Pages: 121
Authors: Larry Kim

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