• Aucun résultat trouvé

Chapter 5. Querying XML with XPath

5.5 XPath Functions

XPath provides numerous functions for working with numbers and strings, and allows you to complete transformations and mine XML data without having to constantly bridge other APIs or technologies to do simple string and arithmetic operations.

Adding, simple division, multiplication, and string searching are available as built-in functions of XPath.

5.5.1 Working with Numbers

Several XPath functions are available to you. In Example 5-4, multiplication is used to apply a 20% discount to products. If you need to total a list of products, you can use the sum function, working with the same products data again:

<?xml version="1.0" encoding="UTF-8"?>

<products>

<item name="bowl" price="19.95"/>

<item name="spatula" price="4.95"/>

<item name="power mixer" price="149.95"/>

<item name="chef hat" price="39.95"/>

</products>

This time, in Example 5-5, you can use a single XPath expression to generate a total.

The expression sum(//@price) returns the sum of the values of all price elements in the products document. Now go back and modify the stylesheet you created to discount the products, but this time add in an xsl:value-of element to generate a total.

Example 5-5. products.xsl

<?xml version="1.0" encoding="iso-8859-1"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">

<html>

<body>

<table>

<xsl:apply-templates/>

</table>

<p>Your Total:

<xsl:value-of select="sum(//@price)"/>

</p>

</body>

</html>

</xsl:template>

<xsl:template match="item">

<tr><td><b>Item: </b><xsl:value-of select="@name"/></td>

<td><b>Price: </b><xsl:value-of select="@price"/></td>

</tr>

</xsl:template>

</xsl:stylesheet>

Figure 5-1 shows the result of the transformation (the HTML) in a browser.

Figure 5-1. Using the sum( ) XPath function

In addition to sum, several other functions exist for working with numbers. The floor function returns the largest integer that is not greater than the argument. In other words, floor(3.4) returns 3. The ceiling function, floor's counterpart, returns the smallest integer that is greater than the argument, e.g., ceiling(3.4) returns 4.

The round function does exactly what you think it should: round your decimal-ridden number to its closest integer. For example, round(3.4) returns 3, while round(3.8) returns 4.

5.5.2 Working with Strings

In addition to functions for numbers, XPath supports functions for manipulating text.

Most of these are valuable when doing conditional testing. Earlier you checked character data of child attributes with syntax such as:

ship[class="Intrepid"]

This expression returns any ship element with a class element beneath it containing the character data Intrepid. This is a fine approach for exact comparisons, but sometimes you'll want finer control.

For example, the starts-with function takes two arguments. The first argument is what you're looking for: the letters the string may start with. The second argument is the node to evaluate. The function returns true or false. For example, to get a true or false return (in XSL) on whether a ship element has a registry code that starts with NCC, you can try the following expression:

<xsl:value-of select="starts-with('NCC', ./registry-code/text( ))"/>

This expression returns true for every ship in the ships.xml file. This type of Boolean return may be of most benefit in XSLT, where you can use its if-then-else language features for conditional processing. A variation on this theme is the contains function, which returns true if the second argument contains the first argument.

If you know you have the string you want and are looking to slice and dice it, the substring and string-length functions can help you out. The substring function takes up to three arguments. The first argument is the string to manipulate; the second argument is the starting index within the string; the third argument is the ending index. If the third argument is omitted, it's assumed to be the end of the string. The string-length function is straightforward, and returns the total length of the string as a number.

The translate function takes a string parameter, as well as a list of characters to replace and a list of corresponding replacement characters. Each character in the second argument is replaced by the corresponding character in the same position in the third argument. For example, the expression translate("Wee Willy Winky",

"eily", "oaps") returns the string Woo Wapps Wanks. The concat function returns the concatenation of its two arguments.

5.5.3 Working with Nodes

Some functions in XPath are designed to work with elements and element traversal itself. These functions supply information related to XPath's current position, and other positional type of information such as first matching element and last matching element. Node functions are fairly straightforward.

The position function returns a number equal to the context position from the expression evaluation context. For example, to create a numbered list for the ships of ships.xml, you could use the position function as shown in the following stylesheet:

<?xml version="1.0" encoding="iso-8859-1"?>

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="shiptypes">

<html>

<body>

<xsl:apply-templates select="ship"/>

</body>

</html>

</xsl:template>

<xsl:template match="ship">

<p>

<xsl:value-of select="position( )"/>.

<xsl:value-of select="@name"/>

</p>

</xsl:template>

</xsl:stylesheet>

This code generates the following HTML output:

<html>

<body>

<p>1.

USS Enterprise</p>

<p>2.

USS Voyager</p>

<p>3.

USS Enterprise</p>

The count function returns the number of nodes in the node set matching the argument. In other words, count(//@name) returns the total number of name attributes within a document. The last function returns a number equal to the context size (the number of nodes) in the current expression.

The id function returns a node by specific id. If you create an element <name id="a345">Chris Jones</name> and then use id('a345') in your expression, this node is returned. The localname and name functions return both the local name and the qualified name of the node in the current node set that appears first in document order.