Chapter 11 - XPath Data Types and Functions | |
XSLT For Dummies | |
by Richard Wagner | |
Hungry Minds 2002 |
Playing ˜Heart and Soul with NodesNodes are the heart and soul of an XSLT transformation, because ultimately, the result document is some sort of arrangement of nodes, whether they are elements, attributes, text, or whatever. Naturally, then, being able to add capabilities beyond what you can do with XSLT elements alone is important. Several functions are available for working with nodes. Getting the current elements positionThe position() function returns the numeric position of the current element and has the following syntax: number position() Tip In this syntax, number describes the return data type (or type of data returned) by the position() function. I show return data types in italics throughout this chapter. The position() function always returns a value based on the current context, which can sometimes be misleading. For example, take the following template rule: <xsl:template match="entree"> <xsl:value-of select="@name"/>'s position: <xsl:value-of select="position()"/> </xsl:template> When applied to the menu.xml document (see Listing 11-1), you might guess that the result document looks something like this: Sunburnt Chicken's position: 1 Filet Mig's None's position: 2 Chicken Parmashaun's position: 3 Eggs Benelux's position: 4 Jerk Chicken's position: 5 Gusto Spaghetti's position: 6 Logical guess, perhaps, but a wrong one. Although the stylesheet focuses on entree elements, dont forget those hidden text nodes that appear between the element nodes. Look again at the source document, and this time Ive removed the children of the entree elements for brevity: <entree name="Sunburnt Chicken"></entree> <entree name="Filet Mig's None"></entree> <entree name="Chicken Parmashaun"></entree> <entree name="Eggs Benelux"></entree> <entree name="Jerk Chicken"></entree> <entree name="Gusto Spaghetti"></entree> When the processor goes through the XML document, it finds the nodes in the following positions : [text node: 1] Sunburnt Chicken's position: 2 [text node: 3] Filet Mig's None's position: 4 [text node: 5] Chicken Parmashaun's position: 6 [text node: 7] Eggs Benelux's position: 8 [text node: 9] Jerk Chicken's position: 10 [text node: 11] Gusto Spaghetti's position: 12 The results that actually display in your result document are: Sunburnt Chicken's position: 2 Filet Mig's None's position: 4 Chicken Parmashaun's position: 6 Eggs Benelux's position: 8 Jerk Chicken's position: 10 Gusto Spaghetti's position: 12 Tip If you find text nodes getting in the way of your position() logic, you can use the xsl: strip-space element to remove them prior to your template rule. See Chapter 13 for more information on xsl:strip-space . Getting the last elementYou can use the last() function to return the final element of the current context. It has the following syntax: number last() Remember The processor evaluates last() and all the other built-in functions when they are within an XPath expression, such as in the select attribute of the xsl:if or xsl:value-of instructions. You cant just type functions inside the template apart from an XSLT instruction; if you do so, the processor treats the function as literal text and doesnt evaluate it. As a general rule, remember that functions are defined only inside quotation marks. In fact, I think built-in functions could have their own Rawhide -like theme song: "Quote em, quote em, quote em. Though the streams are swollen, Keep them doggies rollin, XSLT!" I can use the position() and last() functions together in the following example to list out the entrees in a sentence -like format. Specifically, I want to create a list of the entree elements in which a comma is inserted between entrees, except for the second-to-last entreeit has an and, added instead. For the final entree, a period is added to close the sentence. The template rule containing this logic is shown here: <xsl:template match="menu"> Tonight's entrees are the following: <xsl:for-each select="entree"> <xsl:value-of select="@name"/> <xsl:choose> <xsl:when test="position()=last()"> <xsl:text>.</xsl:text> </xsl:when> <xsl:when test="position()=last()-1"> <xsl:text>, and </xsl:text> </xsl:when> <xsl:otherwise> <xsl:text>, </xsl:text> </xsl:otherwise> </xsl:choose> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> The xsl:choose element tests for the following three conditions:
The formatted results are: Tonight's entrees are the following: Sunburnt Chicken, Filet Mig's None, Chicken Parmashaun, Eggs Benelux, Jerk Chicken, and Gusto Spaghetti. Returning the current nodeYou can return the current node to your code by using the current() function: nodeset current() In the template rule that follows , the xsl:if instruction tests for an entree element that has a name attribute of Jerk Chicken . If so, then the xsl:copy-of instruction uses current() to copy the current node to the result tree: <xsl:template match="entree"> <xsl:if test="./@name='Jerk Chicken'"> <xsl:copy-of select="current()"/> </xsl:if> </xsl:template> Tip In this example, select="current()" is functionally the same as select="." . Getting the node set countYou can use the count() function to get the total number of nodes in the current node set. Its syntax is: number count(nodeset) Tip Anything that appears inside a functions parenthesis is called an argument . The count() function, for example, has nodeset as a single argument. The following template rule lists the total number of menu and entree nodes in the source document: <xsl:template match="/"> menu nodes: <xsl:value-of select="count(//menu)"/> entree nodes: <xsl:value-of select="count(//entree)"/> </xsl:template> The results are: menu nodes: 1 entree nodes: 6 Getting a node nameWhen you add text to your result document, most of the time you are adding the content of the elements or perhaps the values of the attributes. About the only time you think of outputting the elements or attributes themselves is when you are creating an XML output. However, you may have occasions in which you want to treat the name of a node as text. If so, the name() function comes to the rescue, which returns a string value of the specified nodes name: string name([nodeset]) Tip Any parameter that appears in brackets is optional. So, in the case of name() , the nodeset argument may or may not be defined. If it is not, then the current node is the nodeset being evaluated by the function. In the template rule that follows, the name() function comes in handy in creating the header columns for an HTML table: <xsl:template match="menu"> <table> <tr> <th><xsl:value-of select="name(entree/@name)"/></th> <th><xsl:value-of select="name(//diet)"/></th> <th><xsl:value-of select="name(//fatgrams)"/></th> <th><xsl:value-of select="name(//features)"/></th> </tr> <xsl:for-each select="entree"> <tr> <td><xsl:value-of select="@name"/></td> <td><xsl:value-of select="diet"/></td> <td><xsl:value-of select="fatgrams"/></td> <td><xsl:value-of select="features"/></td> </tr> </xsl:for-each> </table> </xsl:template> The resulting table is as follows: <table> <tr> <th>name</th> <th>diet</th> <th>fatgrams</th> <th>features</th> </tr> <tr> <td>Sunburnt Chicken</td> <td>false</td> <td>23</td> <td>Salad, Vegetables, Baked Potato, and Dessert</td> </tr> <tr> <td>Filet Mig's None</td> <td>true</td> <td>0</td> <td>Soup, Vegetables, Baked Potato, and Dessert</td> </tr> <tr> <td>Chicken Parmashaun</td> <td>false</td> <td>20</td> <td>Soup, Pasta, Baked Potato, and Dessert</td> </tr> <tr> <td>Eggs Benelux</td> <td>false</td> <td>35</td> <td>Bacon, Sausage, and Toast</td> </tr> <tr> <td>Jerk Chicken</td> <td>true</td> <td>5</td> <td>Soup, Vegetables, and Dessert</td> </tr> <tr> <td>Gusto Spaghetti</td> <td>false</td> <td>55</td> <td>Soup, Salad, and Dessert</td> </tr> </table>
|