IN THIS CHAPTER
This chapter starts our coverage of the many functions built into XPath 2.0. Although we've seen structural changes in XPath 2.0 compared to XPath 1.0 in the preceding two chapters, on a sheer size basis, there's no question that all the new functions added to XPath 2.0 are the biggest change.
The specification for the XPath 2.0 functions appears in the document XQuery 1.0 and XPath 2.0 Functions and Operators, which is at http://www.w3.org/TR/xquery-operators.
This document is an integral part of the XPath 2.0 specification. Not only are these functions now built into XPath 2.0, but they're also built into XML Query 1.0 and XSLT 2.0. The functions we're going to discuss in this and the following chapters were specifically designed to work on the XPath 2.0 data types, which we covered in Chapter 7, “Introducing XPath 2.0?”
Formally, each function is given by its signature, which shows you how to use the function. A signature shows what parameters you pass to a function, and the type of return value, if there is one:
function-name($parameter-name as parameter-type, ...) as return-type
For example, the signature of the node-name
function, which returns the name of the node you pass to it as a string, looks like this:
node-name($srcval as node) as xs:string
The function name itself for all the functions we'll be taking a look at is an XML QName, followed by parentheses. The functions and operators in the XQuery 1.0 and XPath 2.0 Functions and Operators document use various namespaces, which you should know about.
For data types, the xs
namespace is used for standard XML-schema types, as before; this namespace corresponds to “http://www.w3.org/2001/XMLSchema”. And the xdt
namespace, which corresponds to “http://www.w3.org/2003/05/xpath-datatypes”, is the namespace for the data types specifically added for XPath 2.0.
The namespace for functions is identified by the fn
prefix. Its value is “http://www.w3.org/2003/05/xpath-functions”. You can call functions with this namespace prefix; for example, the node-name
function is referred to as fn:node-name
in the XQuery 1.0 and XPath 2.0 Functions and Operators document:
fn:node-name($srcval as node) as xs:string
Saxon supports many of the XPath 2.0 functions we're going to see, but you do not use the fn
prefix when calling these functions in Saxon.
There's also a fourth prefix defined in the XQuery 1.0 and XPath 2.0 Functions and Operators document—the op
prefix, which corresponds to the namespace “http://www.w3.org/2003/05/xpath-operators for operators”, and which you use with operators. The operator functions are included in the XQuery 1.0 and XPath 2.0 Functions and Operators document to make clear how the various XPath 2.0 operators work on various data types, and they're there to back up the standard operators, which we saw in Chapter 8, “XPath 2.0 Expressions, Sequences, and Operators.” For example, the *
operator on numeric values has a corresponding function named op:numeric-multiply
:
op:multiply($operand1 as numeric, $operand2 as numeric) as numeric
The op
functions are not directly accessible in XPath 2.0, so you cannot call them directly (some software may make them accessible, but Saxon does not). They're in the specification to make it clear how the operators we covered in Chapter 8 work on various data types—we've already covered those operators, so we won't give much coverage to the op
functions here. However, if you need to know exactly how, say, xs:date
values work when you subtract one from another, you can get all the details by looking at how op:subtract-dates
is defined for xs:date
values in the XQuery 1.0 and XPath 2.0 Functions and Operators document.
The XQuery 1.0 and XPath 2.0 Functions and Operators document starts off with the specification for the accessor functions, which work on nodes, and we'll start there too.
The XPath 2.0 Data Model describes a set of accessor functions for use with different types of nodes, and some of those accessor functions are made available through general functions. Here they are:
fn:node-name
returns the name of a node.
fn:string
returns the string value of the argument.
fn:data
takes a sequence of items and returns a sequence of atomic values.
fn:base-uri
returns a node's base URI.
fn:document-uri
returns the document's URI.
We'll take a closer look at these functions here.
The fn:node-name
function returns the expanded name of nodes that can have names (for other node kinds, it returns the empty sequence). Here is the signature for this function—the question mark at the end of the return type indicates in XPath 2.0 that the empty sequence can be returned:
fn:node-name($srcval as node) as xs:QName?
Expanded names consist of a pair of values, a namespace URI, and a local name. In consequence, the way this function actually returns its values is implementation-specific.
The fn:string
function returns the string value of a node. Here's how you use this function—this function can take one argument or an empty sequence, which XPath represents by adding a question mark after the argument type:
fn:string($srcval as item?) as xs:string
Here, fn:string
returns the value of $srcval
represented as a xs:string
. If you don't pass an argument to this function, $srcval
defaults to the context item (which is represented by a dot, .). If you pass an empty sequence, you'll get an empty string, “”, back.
String representations in XPath 2.0 are nearly the same as in XPath 1.0, with some small differences—for example, the representations of positive and negative infinity are now 'INF'
and '-INF'
rather than 'Infinity'
and '-Infinity'
. You can see an example in ch09_01.xsl
, Listing 9.1—in this case, we're displaying the string value of the <planet>
elements in our planetary data document.
Example 9.1. An XSLT Example Using the XPath Function fn:string
(ch09_01.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns :xs="http://www.w3.org/2001/XMLSchema"> <xsl:template match="//planet"> <xsl:value-of select="string(.)"/> </xsl:template> </xsl:stylesheet>
You can see the results when you apply this stylesheet to our planetary data document, including the string value of each <planet>
element:
C:Saxon>java net.sf.saxon.Transform ch02_01.xml ch09_02.xsl <?xml version="1.0" encoding="UTF-8"?> Mercury .0553 58.65 1516 .983 43.4 Venus .815 116.75 3716 .943 66.8 Earth 1 1 2107 1 128.4
This function is handy for turning data of various types into strings. Because of XPath 2.0's reliance on strong data typing, this is an important function—for example, if you want to pass a non-string variable's value to a string function like fn:concat
, you should convert that value to a string using the fn:string
function first.
The fn:data
function converts a sequence of items (which can include nodes and atomic values) into a sequence of atomic values. Here's how you use this function in general—the *
symbol here means “zero or more of,” which is how you represent a sequence in function signatures:
fn:data($srcval as item*) as xdt:anyAtomicType*
If an item in the passed sequence is already an atomic value, it's returned in the returned sequence. On the other hand, if an item in the passed sequence is a node, its typed value is returned (see http://www.w3.org/TR/xpath-datamodel/#dm-typed-value for a description of exactly how typed values for nodes are calculated). If the node has not been validated, its typed value is simply its string value, given the type xdt:untypedAtomic
.
This accessor function returns the base URI for a node. Here's how you use this function:
fn:base-uri($srcval as node) as xs:string?
In XML documents, you set a base URI with the XML xml:base
attribute. You can see an example in ch09_03.xml
, where we're setting a base URI for our planetary data document in a new version, ch09_02.xml
(Listing 9.2).
Example 9.2. Setting a Base URI (ch09_02.xml
)
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="ch01_02.xsl"?>
<planets xml:base="http://www.XPathCorp.com">
<planet>
<name>Mercury</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>
<planet>
<name>Venus</name>
<mass units="(Earth = 1)">.815</mass>
<day units="days">116.75</day>
<radius units="miles">3716</radius>
<density units="(Earth = 1)">.943</density>
<distance units="million miles">66.8</distance>
<!--At perihelion-->
</planet>
<planet>
<name>Earth</name>
<mass units="(Earth = 1)">1</mass>
<day units="days">1</day>
<radius units="miles">2107</radius>
<density units="(Earth = 1)">1</density>
<distance units="million miles">128.4</distance>
<!--At perihelion-->
</planet>
</planets>
Now we can determine the base URI of each <planet>
element in a new XSLT 2.0 stylesheet, as you see in ch09_03.xsl
(Listing 9.3).
Example 9.3. Reading a Base URI (ch09_03.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns :xs="http://www.w3.org/2001/XMLSchema"> <xsl:template match="//planet"> <xsl:value-of select="base-uri(.)"/> </xsl:template> </xsl:stylesheet>
And here's the result when you apply this stylesheet to our new XML document—you can see the base URI of the <planet>
elements in this example:
C:Saxon>java net.sf.saxon.Transform ch09_03.xml ch09_04.xsl <?xml version="1.0" encoding="UTF-8"?> http://www.XPathCorp.com http://www.XPathCorp.com http://www.XPathCorp.com
When you pass it a node, the fn:document-uri
function returns the URI of the document that contains the node, which is useful, for example, if you want to display an error that lists the current document's name. Here's how you use this function:
fn:document-uri($srcval as node) as xs:string?
You can see an example putting fn:document-uri
to work in ch09_04.xsl
(Listing 9.4). Here, we're simply displaying the URI of the current document to test this function.
Example 9.4. Using the fn:document-uri
Function (ch09_04.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns :xs="http://www.w3.org/2001/XMLSchema"> <xsl:template match="/"> <xsl:value-of select="document-uri(.)"/> </xsl:template> </xsl:stylesheet>
And here's the result you get when apply this stylesheet to ch09_02.xml
—in this case the result is the location on disk of ch09_02.xml
:
C:Saxon>java net.sf.saxon.Transform ch09_03.xml ch09_04.xsl <?xml version="1.0" encoding="UTF-8"?> file:/C:/Saxon/ch09_02.xml
The fn:error
function makes the XPath processor display an error message and stop processing. You can pass the error message you want displayed to this function like this—there is no return type, which XPath indicates with a return type of “none” (“none” is a special type defined in XPath 2.0, and it's not actually usable in the XPath language with XPath processors—it's just to indicate there is no return type):
fn:error($srcval as item?) as none
You can see an example in ch09_05.xsl
(Listing 9.5). In this case, we're checking the number of a particular item we have in stock and reporting the results.
Example 9.5. Using the fn:error
Function (ch09_05.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:variable name="inStock" select="80" /> <xsl:template match="/"> <xsl:value-of select="if ($inStock > 0) then concat('Number in stock: ', string($inStock)) else error('Invalid argument')"/> </xsl:template> </xsl:stylesheet>
Here's the result using Saxon as the example is written:
<?xml version="1.0" encoding="UTF-8"?> Number in stock: 80
However, if we change the number in stock to –20 like this:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:variable name="inStock" select="-20" />
<xsl:template match="/">
<xsl:value-of select="if ($inStock > 0)
then concat('Number in stock: ', string($inStock))
else error('Invalid argument')"/>
</xsl:template>
</xsl:stylesheet>
Then running Saxon on this example raises this error and makes Saxon quit:
C:Saxon>java net.sf.saxon.Transform ch09_02.xml ch09_05.xsl Error at xsl:value-of on line 7 of file:/C:/Saxon/ch09_05.xsl: Invalid argument Transformation failed: Run-time errors were reported
In this way, you can cause an error and make the XPath processor report that error—the string we passed to the fn:error
function is displayed by Saxon.
You can use constructor functions to create typed values in XPath 2.0. There is a constructor function for every built-in atomic type except xs:NOTATION
. Here's what they look like:
pref:type($srcval as xdt:anyAtomicType) as pref:type
Here, pref
is the prefix of the type you want to create, either xs
or xdt
. A constructor function has the same name as the type you want to create—for example, to create an xs:double
with a value of 3.1415, you can use the xs:double
constructor function like this:
xs:double("3.1415")
This expression returns an xs:double
value of 3.1415. Here are the legal constructor functions that are supported for the built-in types:
xdt:untypedAtomic(
$srcval
as xdt:anyAtomicType) as xdt:untypedAtomic
xdt:dayTimeDuration(
$srcval
as xdt:anyAtomicType) as xdt:dayTimeDuration
xdt:yearMonthDuration(
$srcval
as xdt:anyAtomicType) as xdt:yearMonthDuration
xs:anyURI(
$srcval
as xdt:anyAtomicType) as xs:anyURI
xs:QName(
$srcval
as xdt:anyAtomicType) as xs:QName
xs:base64Binary(
$srcval
as xdt:anyAtomicType) as xs:base64Binary
xs:boolean(
$srcval
as xdt:anyAtomicType) as xs:boolean
xs:byte(
$srcval
as xdt:anyAtomicType) as xs:byte
xs:date(
$srcval
as xdt:anyAtomicType) as (xs:date, xdt:dayTimeDuration)
xs:dateTime(
$srcval
as xdt:anyAtomicType) as (xs:dateTime, xdt:dayTimeDuration)
xs:decimal(
$srcval
as xdt:anyAtomicType) as xs:decimal
xs:double(
$srcval
as xdt:anyAtomicType) as xs:double
xs:duration(
$srcval
as xdt:anyAtomicType) as xs:duration
xs:ENTITY(
$srcval
as xdt:anyAtomicType) as xs:ENTITY
xs:float(
$srcval
as xdt:anyAtomicType) as xs:float
xs:gDay(
$srcval
as xdt:anyAtomicType) as xs:gDay
xs:gMonth(
$srcval
as xdt:anyAtomicType) as xs:gMonth
xs:gMonthDay(
$srcval
as xdt:anyAtomicType) as xs:gMonthDay
xs:gYear(
$srcval
as xdt:anyAtomicType) as xs:gYear
xs:gYearMonth(
$srcval
as xdt:anyAtomicType) as xs:gYearMonth
xs:hexBinary(
$srcval
as xdt:anyAtomicType) as xs:hexBinary
xs:ID(
$srcval
as xdt:anyAtomicType) as xs:ID
xs:IDREF(
$srcval
as xdt:anyAtomicType) as xs:IDREF
xs:int(
$srcval
as xdt:anyAtomicType) as xs:int
xs:integer(
$srcval
as xdt:anyAtomicType) as xs:integer
xs:language(
$srcval
as xdt:anyAtomicType) as xs:language
xs:long(
$srcval
as xdt:anyAtomicType) as xs:long
xs:Name(
$srcval
as xdt:anyAtomicType) as xs:Name
xs:NCName(
$srcval
as xdt:anyAtomicType) as xs:NCName
xs:negativeInteger(
$srcval
as xdt:anyAtomicType) as xs:negativeInteger
xs:NMTOKEN(
$srcval
as xdt:anyAtomicType) as xs:NMTOKEN
xs:nonNegativeInteger(
$srcval
as xdt:anyAtomicType) as xs:nonNegativeInteger
xs:nonPositiveInteger(
$srcval
as xdt:anyAtomicType) as xs:nonPositiveInteger
xs:normalizedString(
$srcval
as xdt:anyAtomicType) as xs:normalizedString
xs:positiveInteger(
$srcval
as xdt:anyAtomicType) as xs:positiveInteger
xs:short(
$srcval
as xdt:anyAtomicType) as xs:short
xs:string(
$srcval
as xdt:anyAtomicType) as xs:string
xs:time(
$srcval
as xdt:anyAtomicType) as (xs:time, xdt:dayTimeDuration)
xs:token(
$srcval
as xdt:anyAtomicType) as xs:token
xs:unsignedByte(
$srcval
as xdt:anyAtomicType) as xs:unsignedByte
xs:unsignedInt(
$srcval
as xdt:anyAtomicType) as xs:unsignedInt
xs:unsignedLong(
$srcval
as xdt:anyAtomicType) as xs:unsignedLong
xs:unsignedShort(
$srcval
as xdt:anyAtomicType) as xs:unsignedShort
We saw an example in Chapter 7, where we created an xs:date
value like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns :xs="http://www.w3.org/2001/XMLSchema"> <xsl:template match="/"> <xsl:value-of select="xs:date('2004-09-02')"/> </xsl:template> </xsl:stylesheet>
You can also have constructor functions for user-defined types if you derive them by restriction from primitive types. For example, in Chapter 7, we declared a derived type by restriction from the xs:string
type named StateAbbreviation
like this, restricting it to two-character strings like AZ or CA:
<simpleType name='xdat:StateAbbreviation'> <restriction base='xs:string'> <pattern value='[A-Z]{2}'/> </restriction> </simpleType>
After you declare a user-defined type like this, a constructor function is made available to users:
xdat:StateAbbreviation($srcval as xdt:anyAtomicType) as xdat:StateAbbreviation
There is also a set of functions designed to get information about the evaluation context. Here are those functions, some of which we've seen in XPath 1.0:
fn:position
returns the position of the context item within the sequence of items currently being processed.
fn:last
returns the number of items in the sequence of items currently being processed.
fn:current-dateTime
returns the current xs:dateTime
.
fn:current-date
returns the current xs:date
.
fn:current-time
returns the current xs:time
.
fn:default-collation
returns the value of the default collation property from the static context.
fn:implicit-timezone
returns the value of the implicit timezone property from the evaluation context.
We'll take a closer look at these functions here, starting with fn:position
.
XPath 2.0 supports our old friend from XPath 1.0, the fn:position()
function. In XPath 2.0, this function returns an xs:integer
value holding the position of the context item among its siblings:
fn:position() as xs:integer?
Here's an example; say that you wanted to format the names of the planets in our planetary data document like this (note the extra annotation for Venus):
1 Mercury. 2 Venus (the planet of love). 3 Earth.
To make this happen, we'll loop over all the <planet>
elements using an <xsl:for-each>
element:
<xsl:template match="planets"> <xsl:for-each select="planet"> . . . </xsl:for-each> </xsl:template>
We can number each planet's name using the position
function like this:
<xsl:template match="planets"> <xsl:for-each select="planet"> <xsl:value-of select="position()"/> <xsl:text> </xsl:text> <xsl:value-of select="name"/> . . . <br/> </xsl:for-each> </xsl:template>
And we can also add the annotation for Venus like this:
<xsl:template match="planets"> <xsl:for-each select="planet"> <xsl:value-of select="position()"/> <xsl:text> </xsl:text> <xsl:value-of select="name"/> <xsl:value-of select="if (position() = 2) then ' (the planet of love).' else '.'"/> . . . <br/> </xsl:for-each> </xsl:template>
All that's left is to separate each planet's entry onto a different line, and we'll use a <br/>
element for that, as you can see in this example's code, ch09_06.xsl
, which appears in Listing 9.6.
Example 9.6. Using the fn:position
Function (ch09_06.xsl
)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="planets">
<xsl:for-each select="planet">
<xsl:value-of select="position()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="name"/>
<xsl:value-of select="if (position() = 2) then
' (the planet of love).' else '.'"/>
<br/>
<br/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Here's what you get when you apply this stylesheet to our planetary data document:
C:Saxon>java net.sf.saxon.Transform ch02_01.xml ch09_06.xsl <?xml version="1.0" encoding="UTF-8"?> 1 Mercury. <br/> 2 Venus (the planet of love). <br/> 3 Earth. <br/>
The last()
function in XPath 1.0 appears in XPath 2.0 as fn:last()
, which returns an xs:integer
indicating the number of items in the sequence of items currently being processed. Here's its signature:
fn:last() as xs:integer?
first()
FUNCTIONAs in XPath 1.0, there's no first()
function—instead, you test whether the position()
function returns a value of 1.
Here's an example using the last()
function. Say that we want to adapt our previous XSLT example to create an HTML document in which we enclose the planetary list with horizontal rule, <hr/>
, elements. To convert the output of our XSLT stylesheet from XML to HTML, we only need to make the document element of the output document <html>
, which causes the XSLT processor to assume the output document is HTML:
<xsl:template match="planets"> <html> . . . </html> </xsl:template>
Then we just add the other HTML we need, and add an <xsl:if>
element to check for the beginning of the list, where we add the first <hr/>
element:
<xsl:template match="planets"> <html> <head> <title> The Planets </title> </head> <body> <h1> The Planets </h1> <xsl:for-each select="planet"> <xsl:if test="position() = 1"><hr/></xsl:if> <xsl:value-of select="position()"/> <xsl:text> </xsl:text> <xsl:value-of select="name"/> <xsl:value-of select="if (position() = 2) then ' (the planet of love).' else '.'"/> . . . <br/> </xsl:for-each> </body> </html> </xsl:template>
All that's left is to add the final <hr/>
element at the end of the list, and that works as you can see in ch09_07.xsl
(Listing 9.7).
Example 9.7. Using the fn:last
Function (ch09_07.xsl
)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="planets">
<html>
<head>
<title>
The Planets
</title>
</head>
<body>
<h1>
The Planets
</h1>
<xsl:for-each select="planet">
<xsl:if test="position() = 1"><hr/></xsl:if>
<xsl:value-of select="position()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="name"/>
<xsl:value-of select="if (position() = 2)
then ' (the planet of love).'
else '.'"/>
<xsl:if test="position() = last()"><hr/></xsl:if>
<br/>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
To store our results in an HTML document named results.html
, you can use this command with Saxon:
C:Saxon>java net.sf.saxon.Transform ch02_01.xml ch09_07.xsl > results.html
And you can see the result in Figure 9.1.
This function returns the current date and time, with timezone. Here's the signature of this function:
fn:current-dateTime() as dateTime
This function returns an xs:dateTime
value, and you can see an example in ch09_08.xsl
, Listing 9.8, where we're just displaying the current time and date.
Example 9.8. Using the fn:current-dateTime
Function (ch09_08.xsl
)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="current-dateTime()"/>
</xsl:template>
</xsl:stylesheet>
Here's the kind of result you get when you run this example through Saxon:
<?xml version="1.0" encoding="UTF-8"?> 2003-09-19T17:00:25.255Z
The fn:current-date()
function just returns an xs:date
object, with timezone information, that holds the current date. Here's how you use this function:
fn:current-date() as xs:date
You can see an example in ch09_09.xsl
, Listing 9.9, where we're displaying the current date.
Example 9.9. Using the fn:current-date
Function (ch09_09.xsl
)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="current-date()"/>
</xsl:template>
</xsl:stylesheet>
Here's the kind of result you get from Saxon using this example:
<?xml version="1.0" encoding="UTF-8"?> 2003-09-19
As you can gather from its name, the fn:current-time()
function returns the current time. Here's how you use it:
fn:current-time() as time
The return value is an xs:time
value, including timezone. You can see an example in ch09_10.xsl
(Listing 9.10).
Example 9.10. Using the fn:current-time
Function (ch09_10.xsl
)
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="current-time()"/>
</xsl:template>
</xsl:stylesheet>
And here's what you might see when you run this example:
<?xml version="1.0" encoding="UTF-8"?> 17:24:23.503Z
XPath 2.0 also has a number of numeric functions, designed to work with these numeric types:
xs:decimal
xs:integer
xs:float
xs:double
These functions also apply to types derived by restriction from these types.
The XQuery 1.0 and XPath 2.0 Functions and Operators document lists a large number of op
functions that specify how various numeric operators work on different data types. Here are some of the op
functions described—you can tell what operator each function is describing by its name:
op:numeric-add
—. Addition (+)
op:numeric-subtract
—. Subtraction (–)
op:numeric-multiply
—. Multiplication (*)
op:numeric-divide
—. Division (div
)
op:numeric-integer-divide
—. Integer division (idiv
)
op:numeric-mod
—. Modulus (mod
)
op:numeric-unary-plus
—. Unary plus (+)
op:numeric-unary-minus
—. Unary minus (negation) (–)
These functions are not callable from XPath 2.0 expressions; they underpin the operators in the language. However, you can work with a set of numeric functions in the fn
namespace. Each of the fn
functions returns a value of the same type as the type of its argument. If the argument is the empty sequence, the empty sequence is returned, and if the argument is xdt:untypedAtomic
, it is converted to xs:double
.
Here's an overview of the callable numeric functions:
fn:floor
returns the largest number with no fractional part that is less than or equal to the value you pass.
fn:ceiling
returns the smallest number with no fractional part that is greater than or equal to the value you pass.
fn:round
rounds to the nearest number with no fractional part.
fn:round-half-to-even
accepts a number and a rounding precision value and returns a number rounded to the precision you've requested. Note that if the fractional part of the value you pass is .5, the result is the number whose least significant digit is even.
We'll take a look at them here.
The fn:floor
function returns the largest number—that is, the closest to positive infinity—that has no fractional part, such that the returned number is not greater than the value you pass to this function. Here's the signature of this function:
fn:floor($srcval as numeric?) as numeric?
Here are some examples:
fn:floor(2.3) returns 2 fn:floor(1.5) returns 1 fn:floor(-9.5) returns -10
And here's an example putting this function to work in an XSLT 2.0 stylesheet. In this example, we'll determine whether a number is an integer, and we start by assigning that number to a variable named $number
:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="number" select="1.5" />
.
.
.
If floor($number) = $number
, the number is indeed an integer, and we can insert the message "That number is an integer."
in the result document like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="number" select="1.5" /> . . . <xsl:template match="/"> <xsl:value-of select="if (floor($number) = $number) then 'That number is an integer.' . . . </xsl:template>
On the other hand, if floor($number) != $number
, we can insert the message "That number is not an integer."
in the result document, as you see in ch09_11.xsl
(Listing 9.11).
Example 9.11. Using the fn:floor
Function (ch09_11.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="number" select="1.5" /> <xsl:template match="/"> <xsl:value-of select="if (floor($number) = $number) then 'That number is an integer.' else 'That number is not an integer.'"/> </xsl:template> </xsl:stylesheet>
And here's the result when you use Saxon on this stylesheet—as you can see, this code knows that 1.5 is not an integer:
<?xml version="1.0" encoding="UTF-8"?> That number is not an integer.
The fn:ceiling
function returns the smallest—that is, closest to negative infinity—number with no fractional part, such that the returned number is not less than the value you pass this function. Here's what this function's signature looks like:
fn:ceiling($srcval as numeric?) as numeric?
Here are some examples:
fn:ceiling(9.3) returns 10. fn:ceiling(1.5) returns 2. fn:ceiling(-4.5) returns -4.
Here's an example using fn:ceiling
. In this case, say you have one and a half truckloads of iron ore to move and want to know how many trucks you'll need. We start by setting up a variable, $numberofTruckLoads
:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="numberofTruckLoads" select="1.5" />
.
.
.
And passing this variable to the fn:ceiling
function tells us how many trucks we'll need to transport the load; you can see the XPath code in ch09_12.xsl
(Listing 9.12).
Example 9.12. Using the fn:ceiling
Function (ch09_12.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="numberofTruckLoads" select="1.5" /> <xsl:template match="/"> <xsl:value-of select="concat('You will need ', string(ceiling($numberofTruckLoads)), ' trucks.')"/> </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> You will need 2 trucks.
The fn:round
function is XPath 2.0's main rounding function. The fn:round
function returns the number with no fractional part that is closest to the value you pass. If there are two such numbers, this function returns the one closest to positive infinity. Here's what the signature of this function looks like:
fn:round($srcval as numeric?) as numeric?
Here are some examples—note in particular that fn:round(-9.5)
returns –9, not –10:
fn:round(1.2) returns 1. fn:round(6.5) returns 7. fn:round(6.4999999) returns 6. fn:round(-9.5) returns -9.
Here's an example using this function in XSLT 2.0; say you're in charge of the movie reviews of a newspaper and have to multiply the average number of stars awarded to a movie, like 3.73, by your reviewers and come up with a whole number. You can use the fn:round
function to do that, as you see in ch09_13.xsl
(Listing 9.13).
Example 9.13. Using the fn:round
Function (ch09_13.xsl
)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="movieName" select="Casablanca" /> <xsl:variable name="numberOfStars" select="3.73" /> <xsl:template match="/"> <xsl:value-of select="concat('Casablanca got ', string(round($numberOfStars)), ' stars.')"/> </xsl:template> </xsl:stylesheet>
And here's the result:
The fn:round-half-to-even
function gives you a chance to specify the rounding precision yourself. There are two ways to use it—passing it a value, and passing a value and precision:
fn:round-half-to-even($srcval as numeric?) as numeric? fn:round-half-to-even($srcval as numeric?, $precision as integer) as numeric?
The first version of this function returns the nearest value to the value you pass that has no fractional part. If two values are equally close—that is, the fractional part of the value you pass is exactly .5—the value returned is the one whose least significant digit is even.
If you pass a $precision
value to this function, you can set the precision yourself. In this case, the value returned is the nearest value to $srcval
that is a multiple of 1/10 to the power of $pr
e
cision
. As with the other form of this function, if two values are equally close—that is, the fractional part of the value you pass is exactly .5—the value returned is the one whose least significant digit is even.
Here is an example:
fn:round-half-to-even(3.5) returns 4.
On the other hand, the next example also returns 4:
fn:round-half-to-even(4.5) returns 4.
Here's an example that uses a precision of 2 (here we're using the standard exponentiation syntax you use in XPath 2.0; note that 1E+3 = 1000, 1E-1 = 1/10, and so on):
fn:round-half-to-even(9.477512E+3, 2) returns 9477.51E0.
You can also round to positive powers of 10 if you pass a negative value as the precision you want. For example, here's how you can round to the nearest 100:
fn:round-half-to-even(12312.63, -2) returns 12300.
There are also some numeric functions that are specifically designed to work on sequences, called aggregate functions, such as max
, min
, count
, avg
, and sum
, that are coming up in Chapter 12, “XPath 2.0 Node and Sequence Functions.”
The specification for the XPath 2.0 functions is in the document named XQuery 1.0 and XPath 2.0 Functions and Operators, which you can find at http://www.w3.org/TR/xquery-operators.
The namespace for functions is tied to the fn
prefix. Its URL is http://www.w3.org/2003/05/xpath-functions
, and you can call these functions directly (although when you use Saxon, you omit the fn:
prefix). The other prefix defined in the XQuery 1.0 and XPath 2.0 Functions and Operators document is the op
prefix, which identifies the operator functions. These functions are not directly callable; these functions are designed to indicate how the XPath 2.0 operators work with various data types.
The XPath 2.0 functions fn:node-name
, fn:string
, fn:data
, fn:base-uri
, and fn:document-uri
are generic functions that you use with nodes.
The fn:error
function gives you some control over the processing of XSLT stylesheets—calling this function lets you display an error message and halt the XSLT processor.
The context functions return information about the current context. These functions are fn:position
, fn:last
, fn:current-dateTime
, fn:current-date
, fn:current-time
, fn:default-collation
, and fn:implicit-timezone
.
The numeric functions in this chapter. These functions are fn:floor
, fn:ceiling
, fn:round
, and fn:round-half-to-even
.
18.119.107.96