|
Page 2 of 2 Listing 2. Purchase order (sample XML document) <?xml version="1.0" encoding="ISO-8859-1"?> <po:PurchaseOrder xmlns:po="http://www.marchal.com/2006/po"> <po:Buyer>Pineapplesoft<po:Buyer> <po:Seller>Bookstore<po:Seller> <po:OrderLines> <po:Line> <po:Code type="ISBN">0-7897-2504-5<po:Code> <po:Quantity>1<po:Quantity> <po:Description>XML by Example<po:Description> <po:Price>29.99<po:Price> </po:Line> <po:Line> <po:Code type="ISBN">0-672-32054-1</po:Code> <po:Quantity>2<po:Quantity> <po:Description>Applied XML Solutions<po:Description> <po:Price>44.99</po:Price> </po:Line> <po:Line> <po:Code type="ISBN">2-10-005763-4<po:Code> <po:Quantity>2<po:Quantity> <po:Description>Huit Solutions Concrètes avec XML et Java</po:Description> <po:Price>40.00<po:Price> <po:Line> <po:Line> <po:Quantity>1<po:Quantity> <po:Description>Internet Magazine<po:Description> <po:Price>3.10<po:Price> <po:Line> </po:OrderLines> <po:PurchaseOrder>< In Listing 1, for is the keyword; it loops over a sequence of lines and binds each item (each line) to the variable, $product. To select the sequence, the path uses location steps like XPath 1.0 (po:PurchaseOrder/po:OrderLines/po:Line).
Next is the return portion of the expression. The return creates a sequence dynamically. Essentially, it adds zero, one, or more items to the output sequence for every item in the loop. Returning sequences is essential because sequences can be further processed through XPath. For example, it is trivial to compute the total of the purchase order by passing the returned sequence to the sum() function. sum() is an XPath 1.0 function that has been extended to work with sequences, as Listing 3 illustrates: Listing 3. Processing the result of an XPath fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line return $line/po:Price * $line/po:Quantity) What it used to be like
Listing 4 is essentially the same algorithm as Listing 3, but implemented in XPath 1.0 and XSLT 1.0: Listing 4. Computing the total with XPath 1.0 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:po="http://www.marchal.com/2006/po" xmlns:exslt="http://exslt.org/common" version="1.0"> <xsl:output method="text"/> <xsl:template match="/"> <xsl:variable name="lines"> <xsl:for-each select="/po:PurchaseOrder/po:OrderLines/po:Line"> <line-total><xsl:value-of select="po:Price * po:Quantity"/><line-total> <xsl:for-each> </xsl:variable> <xsl:value-of select="sum(exslt:node-set($lines)/line-total)"/> <xsl:template> </xsl:stylesheet> Listing 4 starts by computing the individual line totals and passes the result to the sum() function. However, in XPath 1.0, variables must be declared in the host language (XSLT in this case) so a temporary result set is built into the variable lines. Next, the content of the variable is fed to a second XPath that computes the purchase order total. When comparing Listing 3 and Listing 4, the increased expressiveness of XPath 2.0 is obvious. Listing 4 has two XPath statements instead of one, and it relies on the host language (XSLT) to communicate intermediate results. Listing 4 is less readable and, by breaking the request over two XPath statements, it limits opportunities for query optimization. Conditional expression XPath 2.0 also introduces a conditional expression (if), shown in Listing 5. The syntax is self-explanatory: depending on whether the expression in parenthesis evaluates to true or false, the expression returns the then or else section. Listing 5. Conditional expression if(/po:PurchaseOrder/po:Seller = 'Bookstore') then 'ok' else 'ko' Quantified expressions
A discussion of sequences is not complete without quantified expressions. In a nutshell, quantified expressions are tests that apply to a sequence as a whole. The two quantified expressions are: every and some. Listing 6 is an every expression. It consists of two sections: first binding a variable to a sequence (just like a loop) and then specifying a condition that items in the sequence must meet. The difference between a quantified expression and a loop is that the conditional expression returns a Boolean value, whereas the loop returns a sequence. Specifically, an every expression returns true if the condition is true for every item in the sequence; a some expression returns true if the conditional expression is true for at least one item in the sequence. Listing 6. Quantified expressions every $line in /po:PurchaseOrder/po:OrderLines/po:Line satisfies $line/po:Code Running Listing 6 against the document in Listing 2 returns false because the fourth line does not have a po:Code element. If you were to replace the every keyword with some, then the expression would return true because at least one line has a po:Code element.
Infinite combinations
The power of XPath 2.0 comes from the ability to combine expressions to create sophisticated requests. Listing 7 computes the purchase order total with a different formula: only those lines that include a product code are counted; the other lines are silently ignored (presumably it is not possible to ship those products). The coding is simple because it suffices to add an if expression that returns an empty sequence if the condition is not met. Listing 7. Combining expressions fn:sum(for $line in /po:PurchaseOrder/po:OrderLines/po:Line return if($line/po:Code) then $line/po:Price * $line/po:Quantity else ()) In conclusion, XPath 2.0, thanks to its new data model based on sequences, greatly simplifies writing complex requests. Requests that previously required a lot of XSLT code, you can now write exclusively in XPath.
|