xsl:for-each


The <xsl:for-each> instruction selects a sequence of items using an XPath expression, and performs the same processing for each item in the sequence.

Changes in 2.0

There are no changes to the syntax of this instruction in XSLT 2.0. However, the ability to process sequences of atomic values as well as sequences of nodes greatly increases its power.

Format

 <xsl:for-each   select = sequence-expression>   <!-- Content:(xsl:sort*, sequence-constructor) --> </xsl:for-each> 

Position

<xsl:for-each> is an instruction, which is always used within a sequence constructor.

Attributes

Name

Value

Meaning

select

mandatory

Expression returning a sequence of nodes and/or atomic values

The sequence of items to be processed

Content

Zero or more <xsl:sort> elements, followed by a sequence constructor.

Effect

The effect of the <xsl:for-each> instruction is to evaluate the sequence constructor that it contains once for each item in the selected sequence of items. The following sections describe how this is done.

The select Attribute

The select attribute is mandatory. The expression defines the items that will be processed. This may be any XPath expression, because every XPath expression returns a sequence. It is quite legitimate , and occasionally useful, to select a single item or an empty sequence.

The expression will often be a path expression, which may select nodes relative to the context node (the node currently being processed). Alternatively it may make an absolute selection from the root node, or it may simply select the nodes by reference to a variable initialized earlier. By referencing a tree-valued variable, or by using the document() function (described in Chapter 7, page 532) it may also select the root node of another XML document.

The <xsl:for-each> instruction can also be used to process a sequence of atomic values. The following example causes five empty <br> elements to be output:

  <xsl:for-each select="1 to 5"><br/></xsl:for-each>  

The sequence constructor contained within the <xsl:for-each> element is evaluated once for each item in the select sequence. Within this sequence constructor, the context item is the item being processed (one of the selected items); the position() function gives the position of that item in order of processing (the first item processed has position()=1 , and so on), and the last( ) function gives the number of items being processed.

A common mistake is to forget that <xsl:for-each > changes the context item. For example, the following code will probably produce no output:

  <xsl:for-each select="para">   <p><xsl:value-of select="para"/></p>   </xsl:for-each>  

Why? Because within the <xsl:for-each> element, the context node is a <para> element, so the <xsl:value-of> instruction is trying to display another <para> element that is a child of the first one. What the author probably intended was:

  <xsl:for-each select="para">   <p><xsl:value-of select="."/></p>   </xsl:for-each>  

Sorting

If there are no child <xsl:sort> instructions, the selected items are processed in the order of the sequence produced by evaluating the select expression. If the select expression is a path expression, the nodes will be in document order. In the normal case where the nodes all come from the same input document, this means they will be processed in the order they are encountered in the original source document: For example, an element node is processed before its children. Attribute nodes belonging to the same element, however, may be processed in any order, because the order of attributes in XML is not considered significant. If there are nodes from several different documents in the sequence, which can happen when you use the document() function (described in Chapter 7, page 532), the relative order of nodes from different documents is not defined, though it is consistent if the same set of nodes is processed more than once.

The direction of the axis used to select the nodes is irrelevant. (The direction of different axes is described in Chapter 5 .) For example, «select=" preceding -sibling::*" » will process the preceding siblings of the context node in document order (starting with the first sibling) even though the preceding-sibling axis is in reverse document order. The axis direction affects only the meaning of any positional qualifiers used within the select expression. So «select="preceding-sibling:: *[1]" » will select the first preceding sibling element in the direction of the axis, which is the element immediately before the context node, if there is one.

Although most XPath expressions return nodes in document order, not all do so. For example, the expression «title , author , publisher » returns a sequence containing first the child title elements, then the child author elements, and then the child publisher elements of the context node, regardless of the order that these nodes appear in the source document. The nodes returned by the XPath expression will be processed in the order of the sequence that is returned, not necessarily in document order.

If there are one or more <xsl:sort> instructions as children of the <xsl:for-each> instruction, the items are sorted before processing. Each <xsl:sort> instruction defines one component of the sort key. If the sort key contains several components , they apply in major-to-minor order. For example if the first <xsl:sort> defines sorting by country and the second by state, then the nodes will be processed in order of state within country. If two items have equal sort keys (or if the same item is included more than once in the sequence), they will be processed in the order that they appeared in the original result of the select expression. For a more complete specification of how sorting works, see <xsl:sort> on page 423.

If you want to process the items in the reverse of their original order, specify:

  <xsl:sort select="position()" order="descending">  

Alternatively, call the reverse() function. The following instruction will process the preceding siblings of the context node in reverse document order (that is, starting with the sibling closest to the context node and working backwards ).

  <xsl:for-each select="reverse(preceding-sibling::*)">  

Usage and Examples

The main purpose of <xsl:for-each> is to iterate over a sequence of items. It can also be used, however, simply to change the context item. These two styles of use are illustrated in the following sections.

Iterating over a Sequence of Nodes

The most common use of <xsl:for-each> is to iterate over a sequence of nodes. As such it provides an alternative to <xsl:apply-templates> . Which you use is largely a matter of personal style, arguably <xsl:apply-templates> ( push processing) ties the stylesheet less strongly to the detailed structure of the source document and makes it easier to write a stylesheet that can accommodate some flexibility in the structures that will be encountered, while <xsl:for-each> ( pull processing) makes the logic clearer to the reader. It may even improve performance because it bypasses the need to identify template rules by pattern matching, though the effect is likely to be very small.

The following example processes all the attributes of the current element node, writing them out as elements to the result tree. This example is presented in greater detail under <xsl:element> on page 270.

  <xsl:template match="book">   <book>   <xsl:for-each select="@*">   <xsl:element name="{local-name()}"   namespace="{namespace-uri()}>   <xsl:value-of select="."/>   </xsl:element>   </xsl:for-each>   </book>   </xsl:template>  

The next example is a general one that can be applied to any XML document.

Showing the Ancestors of a Node
start example

The following example stylesheet can be applied to any XML document. For each element it processes all its ancestor elements, in reverse document order (that is, starting with the parent node and ending with the document element), and outputs their names to a comment that shows the position of the current node.

Source

This stylesheet can be applied to any source document.

Stylesheet

This stylesheet is in the file nesting.xsl .

  <xsl:transform   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"   version="1.0"   >   <xsl:template match="*">   <xsl:comment>   <xsl:value-of select="name()"/>   <xsl:for-each select="ancestor::*">   <xsl:sort select="position()" order="descending"/>   <xsl:text> within </xsl:text>   <xsl:value-of select="name()"/>   </xsl:for-each>   </xsl:comment>   <xsl:apply-templates/>   </xsl:template>   </xsl:transform>  

Output

An example of the output this might produce is:

  <!--BOOKS within BOOKLIST-->   <!--ITEM within BOOKS within BOOKLIST-->   <!--TITLE within ITEM within BOOKS within BOOKLIST-->Number, the   Language of Science   <!--AUTHOR within ITEM within BOOKS within BOOKLIST-->Danzig   <!--PRICE within ITEM within BOOKS within BOOKLIST-->5.95   <!--QUANTITY within ITEM within BOOKS within BOOKLIST-->3  
end example
 

Changing the Context Item

Another use of <xsl:for-each> is simply to change the context item. The need for this is reduced in XSLT 2.0, but it is still convenient on occasions. In XSLT 1.0, if you wanted to use the key() function (described in Chapter 7, page 572) to locate nodes in some ancillary document, it was necessary first to establish some node in that document (typically the root) as the context node, because the key() function will only find nodes in the same document as the context node.

For example, you might write:

  <xsl:variable name="county">   <xsl:for-each select="document('county-code.xml')">   <xsl:value-of select="key('county-code', $code)/@name"/>   </xsl:for-each>   </xsl:variable>  

The effect is to assign to the variable the value of the name attribute of the first element whose county-code key matches the value of the $code variable.

In XSLT 2.0 this particular example becomes simpler, because the key() function now accepts a third argument identifying the document to be searched. You can now write:

  <xsl:variable name="county"   select="key('county-code', $code, document('county-code.xml'))/@name"/>  

But there are other cases where the technique is still useful, for example if you need to call a named template that is designed to operate on the context node.

In a stylesheet that handles multiple input documents, it is always a good idea to declare a global variable:

  <xsl:variable name="root" select="/"/>  

Then you can always return to the original source document by writing:

  <xsl:for-each select="$root">   ...   </xsl:for-each>  

See Also

<xsl:apply-templates> on page 187

<xsl:sort> on page 423

document() function in Chapter 7, page 532

key() function in Chapter 7, page 572




XSLT 2.0 Programmer's Reference
NetBeansв„ў IDE Field Guide: Developing Desktop, Web, Enterprise, and Mobile Applications (2nd Edition)
ISBN: 764569090
EAN: 2147483647
Year: 2003
Pages: 324

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