The xsl:for-each Element

The <xsl:for-each> Element

The <xsl:for-each> element enables you to loop over a template body, over and over, for all elements of a node set. Technically speaking, it works on a node set returned by an XPath expression, and performs the same operation on each node in the set. Each time you loop over the template body, its applied to the next node from the node set, making it easy to handle multiple nodes.

<xsl:for-each> Versus <xsl:apply-templates>

You might have noticed that this description is a lot like the one for the <xsl:apply-templates> element, and Ill compare <xsl:for-each> and <xsl:apply-templates> in a few pages.

The <xsl:for-each> element has one attribute:

  • select (mandatory). Set to an XPath expression that returns the node set through which you want to loop.

This element encloses zero or more <xsl: sort > elements, followed by a template body. Youll learn how to use <xsl:sort> later in this chapter.

In the enclosed template body, the position function returns the current node position in a node set, and last returns the number of nodes in a set. If you dont use <xsl:sort> , nodes are processed in document order (the order in which they are listed in the document); if you do use <xsl:sort> , the node set is sorted first as you specify with the <xsl:sort> element.

Say that you want to format all the names of planets inside HTML <P> elements; you can do that as follows :

 <xsl:template match="PLANET">      <P>          <xsl:value-of select="NAME"/>      </P>  </xsl:template> 

However, what if some planets had two names, like this:

 <PLANET>          <NAME>Mercury</NAME>          <NAME>Closest planet to the sun</NAME>          <MASS UNITS="(Earth = 1)">.0553</MASS>          <DAY UNITS="days">58.65</DAY>          <RADIUS UNITS="miles">1516</RADIUS>          <DENSITY UNITS="(Earth = 1)">.983</DENSITY>          <DISTANCE UNITS="million miles">43.4</DISTANCE><!"At perihelion">      </PLANET> 

This is a problem, because the <xsl:value-of> elements select attribute by itself selects only the first <NAME> element; to loop over all possible matches, you can use the <xsl:for-each> element this way instead:

Listing 5.7 Using <xsl:for-each>
 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:template match="PLANETS">          <HTML>              <xsl:apply-templates/>          </HTML>      </xsl:template>  <xsl:template match="PLANET">      <xsl:for-each select="NAME">          <P>              <xsl:value-of select="."/>          </P>      </xsl:for-each>  </xsl:template>  </xsl:stylesheet> 

This style sheet catches all <NAME> elements, places their values in a <P> element, and adds them to the output document this way:

 <HTML>      <P>Mercury</P>      <P>Closest planet to the sun</P>      <P>Venus</P>      <P>Earth</P>  </HTML> 

Heres another example, which first appeared in Chapter 3, Creating and Using Templates, where <xsl: for-each> was used to loop over all attributes in an element:

 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:output method="xml"/>      <xsl:template match="*">          <xsl:copy>              <xsl:for-each select="@*">                  <xsl:copy/>              </xsl:for-each>              <xsl:apply-templates/>          </xsl:copy>      </xsl:template>  </xsl:stylesheet> 

This next example appeared in Chapter 2, Creating and Using Stylesheets. Its a simplified stylesheet, where you cant use any top-level elements, which means that you cant use <xsl:template> or <xsl:apply-templates> , but you can loop over nodes with <xsl:for-each> :

 <HTML xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xsl::version="1.0">      <HEAD>          <TITLE>             The Planets Table          </TITLE>      </HEAD>      <BODY>          <H1>             The Planets Table          </H1>          <TABLE BORDER="2">             <TR>                  <TD>Name</TD>                  <TD>Mass</TD>                  <TD>Radius</TD>                  <TD>Day</TD>             </TR>             <xsl:for-each select="//PLANET">                 <TR>                     <TD><xsl:value-of select="NAME"/></TD>                     <TD><xsl:value-of select="MASS"/></TD>                     <TD><xsl:value-of select="RADIUS"/></TD>                     <TD><xsl:value-of select="DAY"/></TD>                 </TR>              </xsl:for-each>          </TABLE>      </BODY>  </HTML> 

This simplified stylesheet formats planets.xml into planets.html just as well as a template that uses <xsl:apply-templates> , which brings up an interesting question: When do you use <xsl:for-each> to loop over nodes, and when do you use <xsl:apply-templates> ?

In general, its good to use <xsl:apply-templates> when the organization of child nodes is unknown, and you want to apply different templates to different kinds of children, no matter how many levels deep their organization extends. On the other hand, if child nodes have a regular, well-defined organization, you can set up an <xsl:for-each> that handles them all.

The <xsl:for-each> element functions a lot like <xsl:apply-templates> ; you can even nest templates with <xsl:for-each> as you can with successive <xsl:apply-templates> elements. In the following example, I loop over each <PLANET> element, and then loop over all elements contained in the <PLANET> element, listing their data in <DATA> elements this way:

Listing 5.8 Second <xsl:for-each> Example
 <?xml version="1.0"?>  <xsl:stylesheet version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">      <xsl:output method="xml"/>      <xsl:template match="PLANETS">          <PLANETS>              <xsl:for-each select="PLANET">                  <PLANET>                      <xsl:for-each select="*">                          <DATA>                              <xsl:value-of select="."/>                          </DATA>                      </xsl:for-each>                  </PLANET>              </xsl:for-each>          </PLANETS>      </xsl:template>  </xsl:stylesheet> 

And heres the result:

 <?xml version="1.0" encoding="UTF-8"?>  <PLANETS>      <PLANET>          <DATA>Mercury</DATA>          <DATA>.0553</DATA>          <DATA>58.65</DATA>          <DATA>1516</DATA>          <DATA>.983</DATA>          <DATA>43.4</DATA>      </PLANET>      <PLANET>          <DATA>Venus</DATA>          <DATA>.815</DATA>          <DATA>116.75</DATA>          <DATA>3716</DATA>          <DATA>.943</DATA>          <DATA>66.8</DATA>      </PLANET>      <PLANET>          <DATA>Earth</DATA>          <DATA>1</DATA>          <DATA>1</DATA>          <DATA>2107</DATA>          <DATA>1</DATA>          <DATA>128.4</DATA>      </PLANET>  </PLANETS> 


Inside XSLT
Inside Xslt
ISBN: B0031W8M4K
EAN: N/A
Year: 2005
Pages: 196

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