Chapter 5 - XPath Espresso | |
XSLT For Dummies | |
by Richard Wagner | |
Hungry Minds 2002 |
The final part of the location step is the predicate , which can be optionally defined. If you use a predicate, it separates the wheat from the chaff in the resulting node setkeeping the wheat for the result tree and throwing away the chaff. After the processor evaluates the predicate, which is an XPath expression, the processor converts the result into a Boolean (true or false) value. Those nodes with a true value are included in the result tree, and those with a false value are not. For example, using xsltfordummies-toc.xml (see Listing 5-1), suppose you wanted to output just the contents of Part V of the book in your result tree. Using a predicate, you define the template rules match pattern as part[@number='V'] , which tells the processor to return all the part elements that have a number attribute with the value of V . This template rule then returns only the specific part element you want. However, you also need to create an empty template rule to override the built-in template rule for the other part nodes: <xsl:template match="part[@number='V']"> Part V includes: <xsl:apply-templates/> </xsl:template> <xsl:template match="isbn"/> <xsl:template match="part"/> The end result is: Part V includes: Ten Most Confusing Things About XSLT Ten All-Pro XSLT Resources On The Web Ten Free XSLT Processors You Can Download Remember Both of these template rules have match patterns with the same axis::node test values, resulting in a conflict. However, because a predicate is defined for the first rule, it gets priority for any nodes that match both rules. (See Chapter 4 for more information on priorities.) You can also use built-in XPath functions inside predicates. I talk about these in Chapter 11, but I use the not() built-in function here to give you a flavor for how you can use them. The not() function returns the opposite value of the XPath expression inside it. So, whereas the location step book[@id] is used to return all book elements that have an id attribute, book[not(@id)] returns all book elements that do not have an id attribute. Applying this principle to a full example, consider the following: <xsl:template match="chapter"> Chapter <xsl:apply-templates select="@number"/> : Summary provided </xsl:template> <xsl:template match="chapter[not(summary)]"> Chapter <xsl:apply-templates select="@number"/> : No summary provided </xsl:template> This set of template rules formats the output differently based on whether or not the chapter element has a child summary element. Because Chapters 18 to 20 are the only ones that dont have a summary, the output looks like: Chapter 1 : Summary provided Chapter 2 : Summary provided Chapter 3 : Summary provided Chapter 4 : Summary provided Chapter 5 : Summary provided Chapter 6 : Summary provided Chapter 7 : Summary provided Chapter 8 : Summary provided Chapter 9 : Summary provided Chapter 10 : Summary provided Chapter 11 : Summary provided Chapter 12 : Summary provided Chapter 13 : Summary provided Chapter 14 : Summary provided Chapter 15 : Summary provided Chapter 16 : Summary provided Chapter 17 : Summary provided Chapter 18 : No summary provided Chapter 19 : No summary provided Chapter 20 : No summary provided You can also use predicates to select a specific node based on its position in the node set. For example, if you want to select the first chapter child of the current node, you can use the position() built-in function: chapter[position()=1] As a shortcut, you can simply leave off the function name and just specify a position value: chapter[1] When the processor sees a numeric value by itself in the predicate, it implicitly adds the position()= . Or to select the last chapter child, use another built-in function, last() : chapter[last()] As you work with positions in a node set, the order of the returning nodes becomes very important. So, when you use the reverse order axes ( ancestor , ancestor-or-self , preceding , and preceding-sibling ), remember that [1] selects the first node in reverse order. For example, consider the following XML snippet: <part number="II" name ="Becoming An XSLT Transformer"> <chapter number="3"> <title>Transforming With Style (Stylesheets, that is)</title> </chapter> <chapter number="4"> <title>Templates Rule!</title> </chapter> <chapter number="5"> <title>XPath Espresso</title> </chapter> <chapter number="6"> <title>We Want Results!</title> </chapter> </part> With <chapter number="4"> element as the current node, preceding-sibling returns the <chapter number="3"> and following-sibling returns a node set with <chapter number="5"> and <chapter number="6"> elements. Using this snippet as the source, suppose you want to output the title of each chapter along with the titles of the previous and next chapter. The template rule is defined as follows : <xsl:template match="chapter"> Previous: <xsl:value-of select="preceding- sibling::chapter[1]/title"/> Current: <xsl:apply-templates select="title"/> Next: <xsl:value-of select="following- sibling::chapter[1]/title"/> </xsl:template> The template rule uses chapter as the location step to return all chapter elements for the template. Following the literal text Previous: , I use an xsl:value-of instruction to convert the result of select to a string. The preceding-sibling::chapter[1] step finds the first sibling that occurred before it on the source tree. The value of its child title element is then used by xsl:value-of . For the next chapter, I use following-sibling and apply the same logic as before. The end result is: Previous: Current: Transforming With Style (Stylesheets, that is) Next: Templates Rule! Previous: Transforming With Style (Stylesheets, that is) Current: Templates Rule! Next: XPath Espresso Previous: Templates Rule! Current: XPath Espresso Next: We Want Results! Previous: XPath Espresso Current: We Want Results! Next:
|