IN THIS CHAPTER
The most basic expression type is the primary expression type. This type can be one of these:
A literal
A variable
A function call
A context item
A parenthesized expression, which is used to control precedence of operators
A comment
To understand these in detail, we'll take a look at them one by one.
A literal is just a value that is of an atomic type. As we saw in Chapter 7, “What's New in XPath 2.0,” this means a value of a primitive simple type defined by the XML Schema specification, or a value of a type derived from them by restriction in a schema.
XPath 2.0 has built-in support for two types of literals—numeric literals and string literals. You can create a string literal simply by enclosing a string of characters in quotes, and numeric literals simply by writing the numeric value. You can see a simple example in ch08_01.xsl
(Listing 8.1), which uses a string literal as an XPath expression.
Example 8.1. An XSLT Example Using an XPath Literal (ch08_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="/">
<xsl:value-of select="'No worries!'"/>
</xsl:template>
</xsl:stylesheet>
A string literal's value is an xs:string
atomic type. You can simply enclose a string of characters in quotes, but be careful about nested quotes. For example, this literal is okay: “This is fine.” You can also use single quotes like this: 'This is also fine.' But what if you want to use quotation marks inside a string literal? In that case, you can nest single and double quotes, like this:
"I said, 'This literal is OK.'"
You can also use double quotation marks to indicate quoted text. For example, this string literal works just as the previous one did, except that the internal quoted text is delimited by two double quotes:
"I said, ""This literal is OK."""
Doubling single quotation marks works the same way:
'I said, ''This literal is OK.'''
By default, numeric literals that don't contain a “.” and contain no “e” or “E” (used to specify exponents), are of type xs:integer
. For example, here are some integer numeric literals:
345 1 -9999
On the other hand, if your numeric value includes a “.”, but no “e” or “E”, XPath 2.0 assumes the literal is of the xs:decimal
data type. Here are a few examples:
3.14159 -1.000 2.7128
If you include an “e” or an “E” character, XPath 2.0 assumes your numeric literal is an xs:double
type. Here are a few examples:
3.14159E10 55.00e45 1.9e-5
Besides string and numeric literals, you can also use Boolean values, representing them with the built-in functions true()
and false()
.
You can also create literals of any of the XML schema types using their constructors. We saw an example of how that works in Chapter 7, where we created an xs:date
literal:
<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>
Here are a few other examples of creating literals based on XML schema types using the appropriate constructors:
xs:integer(45) xs:decimal(3.14159) xs:string("No worries!")
Variables are also expressions in XPath 2.0—the value of the expression is the value stored in the variable. In XPath 2.0, variables are valid XML names (that is, an XML QName), preceded by a $.
There are two sources of variables in XPath 2.0—the host language (such as XSLT), and those variables that you can define in XPath itself. For example, we've seen in Chapter 7 that you can create variables in XSLT using the <xsl:variable>
element, and use them in XPath 2.0 like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:variable name="rightNow" select="current-dateTime()" /> <xsl:template match="/"> The date and time is: <xsl:value-of select="$rightNow"/> </xsl:template> </xsl:stylesheet>
You can also use support for variables in XPath 2.0, as in these for
and some
expressions:
for $variable in /planets/planet return $variable/mass some $variable in /planets/planet[1]/name satisfies $variable = "Mars"
When you're using multiple operators, such as multiple arithmetic operators, there can be some confusion about which operator is applied first. For example, what does 1 + 2 * 3 equal—3 * 3 or 1 + 6? It turns out that the multiplication operator is evaluated first, so 1 + 2 * 3 = 1 + 6 = 7.
As in various programming languages, variables in XPath 2.0 also have scope, which is the region in which the XPath processor can access them. For example, in the for
expression in the preceding code, the variable named $variable
is in scope only inside the body of the return
clause—outside that body, you can't use this variable. In the some
expression in the preceding code, $variable
is in scope in the satisifies
clause's body.
On the other hand, if you wanted to add the 1 and 2 before multiplying the result by 3, you could use parentheses to indicate the order of execution you want:
(1 + 2) * 3
In XPath 2.0, parenthesized expressions like these are valid. (And don't forget that you can use an empty set of parentheses—()
—to represent an empty sequence.)
Function calls are also considered expressions in XPath 2.0, and their value is simply the value returned by the function. For example, we took a look at the XPath 2.0 built-in max
function in Chapter 7:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="planets"> <HTML> <HEAD> <TITLE> The Largest Planetary Radius </TITLE> </HEAD> <BODY> <H1> The Largest Planetary Radius </H1> <BR/> The largest planetary radius is <xsl:value-of select="max(//planet/radius)"/> miles. </BODY> </HTML> </xsl:template> </xsl:stylesheet>
Here, the value of the function call is the sequence it returns.
Note that you can pass not only atomic values to functions, but also sequences. For example, this function call passes the values 4, 5, and 6 to a function:
function(4, 5, 6)
But this example passes a sequence consisting of 4 and 5, and then a value of 6:
function((4, 5), 6)
The context item expression is simply a dot, “.”, and as in XPath 1.0, it stands for the current context item. This can be a context node (the node from which evaluation of an expression starts), as in the path expression //planet[count(./name) > 1]
.
In XPath 2.0, context items can also be atomic values, as in this expression: (1 to 100)[. mod 10 eq 0]
, which returns the sequence (10, 20, 30, 40, 50, 60, 70, 80, 90, 100).
Besides primary expressions, XPath 2.0 also supports arithmetic operators for addition, subtraction, multiplication, division, and modulus. These operators are represented this way (note that instead of using the /
symbol for division, which can be interpreted as markup, XPath 2.0 uses the div
operator):
+
—. Addition
-
—. Subtraction and Negation
*
—. Multiplication
div
—. Division
idiv
—. Integer division
mod
—. Modulus
In XPath 2.0, you must precede a subtraction operator with a space, because “-” is a legal name character. In other words, x-y
can be interpreted as a name, whereas x - y
is an expression involving a subtraction operation.
The idiv
operator requires its operands to be of type xs:integer
and returns a result of type xs:integer
, rounded toward zero. And the mod
operator returns the remainder after a division (for example, 16 mod 3 is 1).
Here are some examples of arithmetic expressions:
As in XPath 1.0, you can use a path expression to locate nodes. In fact, XPath 2.0 carries over just about all the same syntax for path expressions from XPath 1.0, although, of course, node-sets are now called sequences and there are other changes, such as deprecating (making obsolete) the namespace axis.
As in XPath 1.0, path expressions are made up of location steps. In turn, location steps are made up of an axis, a node test, and zero or more predicates:
axis :: node-test [predicate]*
If you start a location path with /
or //
, the location path is an absolute location path because you're a specifying the path from the root node of the XML document. Otherwise, the location path is relative, starting with the context node.
Each XPath 2.0 path expression must specify an axis (or use the default child
axis), as in this location path: /library/book/title[2]/text
. The XPath 2.0 axes are the same as the XPath 1.0 axes.
Note in particular that the namespace
axis is now deprecated in XPath 2.0. That means that it's in a kind of limbo, preparatory to being phased out. Whether or not the software package you're using supports the namespace axis is now up to the package itself—it no longer has to.
When you use an axis in a location step, you're telling XPath 2.0 where to look and identifying a set of nodes. As in XPath 1.0, a node test tells XPath 2.0 which of the nodes in that set you're interested in.
There are two ways of creating node tests in XPath 2.0. You can use node names as node tests, along with a wildcard character, *
, as in XPath 1.0. Or you can use “kind” tests like comment()
, text()
, and so on, as also in XPath 1.0. Here's an overview of the kinds of node tests you can use in XPath 2.0—note that element()
, attribute()
, and document-node()
are new in XPath 2.0:
A name matches a node with that name (for example, planet
will match a <planet>
element).
The *
wildcard character matches any node of the principal node kind—elements, attributes, or namespaces. For example, child::*
will select all element children of the context node, and attribute::*
will select all attributes of the context node. You can also use *
with the namespace axis.
The comment()
node test selects comment nodes.
The node()
node test selects any type of node.
The processing-instruction()
node test selects a processing instruction node. You can specify the name of the processing instruction to select in the parentheses.
The text()
node test selects a text node.
The element()
node test selects elements.
The attribute()
node test selects attributes.
In XPath 2.0, you can pass parameters to the kind node tests element()
and attribute()
. Here are some examples (for more on type annotations, see the discussion on them in Chapter 7):
element(planet)
matches any element node whose name is planet and whose type annotation conforms to the schema declaration for a <planet>
element.
element(planet, *)
matches any element node whose name is planet without any restriction on type annotation.
element(planet, giant)
matches any element node whose name is planet, and whose type annotation is giant
or is derived from giant
.
element(*, giant)
matches any element node whose type annotation is giant
, regardless of its name.
element(solarsystem/planet)
matches any element node whose name and type annotation conform to the schema declaration of a <planet>
element in a <solarsystem>
element.
attribute()
matches any attribute node.
attribute(language, *)
matches any attribute whose name is language
, regardless of its type annotation.
attribute(*, xs:decimal)
matches any attribute whose type annotation is xs:decimal
or derived from xs:decimal
.
document-node()
matches any document node.
document-node(element(planet))
matches any document node whose content consists of a single element node that satisfies the element(planet) node test.
As in XPath 1.0, the next part of a location step, which follows the node text, is the predicate. A location step doesn't need a predicate, but using predicates, you can filter the nodes you want to locate even more.
Predicates are where you use the many XPath 2.0 functions when you want to use them in path expressions. This works as it does in XPath 1.0; for example, in the location step child::planet[position() = 2]
, the predicate is position() = 2
. This means that the value the built-in XPath function position()
returns must indicate that this is the second <planet>
child in order for the location step to match. As in XPath 1.0, this location step can also be abbreviated as planet[2]
.
The rules for abbreviated syntax in XPath 2.0 are the same as in XPath 1.0 (technically speaking, .
is not actually an abbreviation—it's the context node item, as discussed earlier.):
self::node()
can be abbreviated as.
parent::node()
can be abbreviated as ..
child::
nodename
can be abbreviated as nodename
attribute::
nodename
can be abbreviated as @node
name
/descendant-or-self::node()/
can be abbreviated as //
As you know from XPath 1.0, you can also abbreviate position expressions like [position() = 6]
as [6]
. That's all there is to abbreviated syntax. The next type of XPath 2.0 expression we'll take a look at is sequence expressions.
As we've seen in Chapter 7, you can create sequence expressions with the comma (,) operator. In XPath 2.0, the comma operator evaluates each of its operands and joins the results into a sequence.
You can use empty parentheses to indicate an empty sequence. Also note that sequences can never be nested—for example, combining the values 4, (5, 6), into a single sequence results in the sequence (4, 5, 6). Sequences can contain nodes or atomic values, or a mix.
Sequences may contain duplicate values or nodes. When you create a new sequence by concatenating two or more input sequences, the new sequence contains all the items in the original sequences.
We'll start our work on sequence expressions by seeing how to create sequences with them.
Here are a few examples; this sequence expression creates a sequence of six integers:
(9, 10, 11, 12, 13, 14)
In this case, we're creating a new sequence from the sequences 1, (2, 3), ()
—that is, an empty sequence—and (4, 5, 6):
(1, (2, 3), (), 4, 5, 6))
This expression gives you the following sequence:
1, 2, 3, 4, 5, 6
Here's how you could create a sequence made up of all the <name>
children of the context node, followed by all the <mass>
children:
(name, mass)
You can also use variables or other expressions when creating sequences, like this:
($temperature, 3 * 6)
If $temperature
holds 68, this example gives you this sequence:
(68, 18)
You can also use the range operator, as mentioned in Chapter 7, to create a sequence of integers. Here's an example:
(1 to 5)
This sequence expression creates the following sequence:
(1, 2, 3, 4, 5)
You can also use the range operator inside a sequence expression, like this:
(1, 2 to 5)
Here's the result:
1, 2, 3, 4, 5
What if you were to make the start and end of the range the same value by mistake? For example, how is XPath 2.0 supposed to handle this?:
9999 to 9999
In this case, the result is a singleton sequence with just one item, 9999.
XPath 2.0 also gives you a set of operators to work with sequences, as we saw in the overview in Chapter 7. We'll take a look at these operators—union
, intersect
, and except
—which create sequence expressions, next.
The union
, intersect
, and except
operators return their results as sequences in document order, without any duplicate items in those sequences.
The union
operator takes two node sequences as operands and returns a sequence containing all the nodes that occur in either of the original sequences. This operator works something like the logical or
operator in other languages. The union
operator is identical to the |
operator.
Here are a few examples, where a
, b
, c
, d
, and e
are nodes:
a, b, union c, d, e
This expression gives you
a, b, c, d, e
On the other hand, duplicate items do not appear in the resulting sequence. This expression
a, b, c union c, d, e
gives you
a, b, c, d, e
The intersect
operator takes two node sequences as operands and returns a sequence containing all the nodes that occur in both operands. This operator works like the logical and
operator in other languages.
Here are a few examples; in this expression, both the nodes b
and c
are common to both sequence operands:
a, b, c intersect b, c, d
And here's the result:
b, c
This sequence combination expression
d, e, f intersect b, c, d
gives you just a singleton sequence:
d
If you try to find the intersection of two sequences that have no items in common, like this:
a, b intersect c, d
the result will be an empty sequence, ()
.
The except
operator takes two node sequences as operands and returns a sequence containing all the nodes that occur in the first operand but not in the second operand.
Here's an example:
a, b, c except c
gives you
a, b
Here's another example:
a, b, c, d except c, d, e
gives you
a, b
This expression
a, b, c except d, e, f
gives you
a, b, c
Besides the sequence operators we're discussing here that are evaluated as XPath expressions, there's also a set of functions that support indexed access to items or subsequences of a sequence, indexed insertion or removal of items in a sequence, and removal of duplicate values or nodes from a sequence. They're coming up in Chapter 12, “XPath 2.0 Node and Sequence Functions.”
And this expression
a, b, c except a, b, c, d, e
Comparison expressions return the result of comparing two values. XPath 2.0 actually supports four kinds of comparison expressions: value comparisons, general comparisons, node comparisons, and order comparisons.
We'll take a look at them all here, starting with value and general comparisons. These two types of comparisons have been added so that XPath 2.0 can support comparisons with both single values and with sequences.
You use the value comparison operators when you're working with atomic values. Here they are:
eq
—. Equals
ne
—. Not equals
lt
—. Less than
le
—. Less than or equal to
gt
—. Greater than
ge
—. Greater than or equal to
These operators give you a result of true
or false
. Here's an example—say that $temperature
holds the value 68; in that case, this expression would evaluate to true
:
$temperature lt 72
This comparison is true only if $planet
has a single <name>
child element and its value is “Venus”:
$planet/name eq "Venus"
You can use general comparisons on sequences (including singleton sequences). Here are the general comparisons:
=
—. Equals
!=
—. Not equals
<
—. Less than
<=
—. Less than or equal to
>
—. Greater than
>=
—. Greater than or equal to
As in XPath 1.0, when you use XPath expressions inside an XML document, the XML escaping rules for special characters should be followed. For example, “<” should be written as “<”.
You use these operators on sequences. (What actually happens is that a value comparison operator, eq
, ne
, lt
, le
, gt
, or ge
—depending on which corresponding general comparison operator was used—is used to compare individual items in the sequence.) The software evaluating a general comparison usually will return a value of true
as soon as it finds an item in the first operand and an item in the second operand for which the value comparison is true.
Here's an example pointing out how these operators deal with sequences, not just individual values. In this case, we're comparing two sequences, (1, 2) and (2, 3) with the general equality operator:
(1, 2) = (2, 3)
In this case, the result is true because the value 2 appears in both sequences.
Here, however, the result is false, because there is no value in the first sequence that is equal to a value in the second:
(1, 2) = (3, 4)
As with value comparisons, this comparison is true only if $planet
has a single <name>
child element and its value is “Venus”:
You can use node comparison expressions to compare nodes using the is
operator.
A comparison expression with the is
operator is true if the two operands are nodes that are identical; otherwise it is false.
For example, this comparison is true only if the left and right sides each evaluate to exactly the same single node:
//planet[name="Venus"] is //planet[days=116.75]
You use order comparison expressions to compare the order of nodes; both operands here must be either a single node or an empty sequence (if either operand is an empty sequence, the result of the comparison is an empty sequence). Here are the order comparison operators:
<<
—. Earlier than
>>
—. Later than
Using the <<
operator returns true
if the first operand node is earlier than the second operand node in document order; otherwise it returns false
. Using the >>
operator returns true
if the first operand node is later than the second operand node in document order; otherwise it returns false
.
Here's an example:
//planet[name="Venus"] << //planet[days=116.75]
This example returns true if the node matched by //planet[name="Venus"]
comes earlier in document order than the node matched by //planet[days=116.75]
.
A logical expression uses logical operators and is a compound expression. Unless there's an error, its value is always either true
or false
. Here are the logical operators:
and
—. Performs a logical and
operation
or
—. Performs a logical or
operation
The and
operator lets you connect two Boolean operands and returns true
if both operands are true, and false
otherwise. The or operator lets you connect two logical operands and returns true
if either operand is true, and false
otherwise.
Here's an example. Say that $temperature
holds a value of 72; in that case, this example returns a value of true
:
$temperature < 80 and $temperature > 60
And this expression also returns true
, because one logical operand ($temperature > 60
) is true:
$temperature > 80 or $temperature > 60
not
FUNCTIONXPath 2.0 also has a function named not()
, which reverses the Boolean value of the value you pass to it. For example, if $temperature
holds a value of 72, then $temperature = 72
is true, and not($temperature = 72)
is false. More on this function is coming up in Chapter 11, “XPath 2.0 Boolean, QName, and Date Functions.”
This expression returns false
:
$temperature > 80 and $temperature > 60
XPath provides the for
expression so that you can iterate over data. Here's how the for
expression works in general:
for variable in sequence return expression
Here, the variable is called the range variable, the value of the expression that follows the in
keyword is called the input sequence, and the expression that follows the return
keyword is called the return expression.
The result of the for
expression is obtained by evaluating the return expression once for each item in the input sequence. The resulting sequence is returned (if multiple sequences are generated, they are concatenated).
You can see an example in ch08_02.xsl
, where we're using a for
expression to create a sequence of all the planet names in our planetary data XML document:
for $variable in //planet return $variable/name
To display our results using an XPath 2.0 style sheet, we'll use the <xsl:value-of>
element to insert the result sequence of names into the output. That means we have to use this element's separator
attribute to indicate what text we want inserted between items in the sequence—if you don't use this attribute, you'll only get the first item in the sequence. You can see what the XSLT 2.0 style sheet looks like in ch08_02.xsl
(Listing 8.2).
Example 8.2. An XSLT Example Using the for
Expression (ch08_02.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="for $variable in //planet return $variable/name" separator=", "/> </xsl:template> </xsl:stylesheet>
And here's the result when we use Saxon to apply the ch08_02.xsl
style sheet to our planetary data document:
C:Saxon>java net.sf.saxon.Transform ch02_01.xml ch08_02.xsl <?xml version="1.0" encoding="UTF-8"?> Mercury, Venus, Earth
As you can see, we do indeed get all three planetary names this way. A for
expression can also use multiple variables. For example, this expression uses two variables at once:
for $x in (1, 2) $y in (3, 4) return ($x * $y)
The result of this for
expression is the sequence (3, 4, 6, 8).
As we saw in the overview in Chapter 7, XPath 2.0 supports a conditional expression that uses the keywords if
, then
, and else
. Here's what this expression, also called the if
expression, looks like in its general form:
if expression then then-expression else else-expression
The expression following the if
keyword is called the test expression, and the expressions following the then
and else
keywords are called the then-expression and else-expression, respectively.
If the value of the test expression is true, the value of the then-expression is returned. If the Boolean value of the test expression is false, the value of the else-expression is returned.
Here's an example using a conditional expression in an XSLT 2.0 stylesheet. In this case, we'll declare an XSLT variable named $temperature
that holds the value 80:
<xsl:variable name="temperature" select="80" />
Now we'll test that new variable's value in a conditional expression, evaluating to the text “Too hot” if the temperature is above 72, and “OK” otherwise:
if ($temperature > 72) then 'Too hot' else 'OK'
You can see what the complete XSLT 2.0 stylesheet looks like in ch08_03.xsl
(Listing 8.3).
Example 8.3. An XSLT Example Using the if
Expression (ch08_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:variable name="temperature" select="80" /> <xsl:template match="/"> <xsl:value-of select="if ($temperature > 72) then 'Too hot' else 'OK'"/> </xsl:template> </xsl:stylesheet>
And here's the result you get when you use Saxon:
<?xml version="1.0" encoding="UTF-8"?> Too hot
You can use any kind of a test expression, as long as it evaluates to a true
/false
value. For example, in this case, we're checking which of two planets has the greater mass, and returning the one that does:
if (//planet[1]/mass > //planet[2]/mass) then //planet[1] else //planet[2]
You don't have to compare values either; you can simply test for the existence of an item, as here, where we're testing if a <planet>
element has a <name>
child element:
if (//planet[1]/name) then //planet[1]/name else //planet[2]/name
You can also nest if
expressions, as in this example:
if ($fruit eq "apple") then "It's an apple." else if ($fruit eq "orange") then "It's an orange." else "I have no idea what this is."
Quantified expressions use the some
and every
keywords to perform checks on the items in a sequence. You can use the some
keyword to ensure that at least one of the items in the sequence satisfies a particular criterion, and the every
keyword to make sure that every item in the sequence satisfies some criterion.
Here's how you use the some
quantified expression:
some in-claus(es) statisfies test-expression
A quantified expression like this starts with the some
keyword, followed by one or more in-clauses, followed by the keyword satisfies
, followed by a test-expression. What this expression does is to let you test if at least one item in the in-clause(s) satisfies the test expression.
Let's take a look at a few examples to make this clear. This expression is true if at least one of the <planet>
elements in the document has a <name>
child element:
some $planet in //planet satisfies $planet/name
Here's an example that tests if at least one <planet>
element has a language
attribute:
some $planet in //planet satisfies $planet/@language
In this way, you can use quantified expressions to test for the existence of items like elements and attributes.
You can also test logical conditions, as here, where we're making sure that at least one planet has a mass greater than 1.5:
some $planet in //planet satisfies ($planet/mass > 1.5)
Here's another example:
some $planet in //planet satisfies ($planet/name = "Mars")
You can also use multiple in-clauses in a some
expression. Here's an example, where we're testing the product of numbers:
some $operand1 in (4, 5, 6), $operand2 in (7, 8, 9) satisfies $operand1 * $operand2 = 40
some
EXPRESSIONAlthough this is implementation-specific, the software may terminate a some
expression as soon as it is able to satisfy the test expression, without evaluating all possible values.
In this case, all possible combinations of the items in the in-clauses are tested—nine combinations in all. That is, 4 * 7 is tested, then 4 * 8, 4 * 9, then 5 * 7, 5 * 8, and so on.
Here's how you use the every
quantified expression:
every in-claus(es) statifies test-expression
This quantified expression starts with the every
keyword, followed by one or more in-clauses, followed by the keyword satisfies
, followed by a test-expression. This expression lets you test if every item in the in-clause(s) satisfies the test expression.
Here are a few examples. This expression is true if every one of the <planet>
elements in a document has a <name>
child element:
every $planet in //planet satisfies $planet/name
As with the some
expression, you can either test for the existence of an item this way, or you can test a logical condition. This every
expression tests whether planet's <day>
value is greater than or equal to one:
every $planet in //planet satisfies ($planet/day ge 1)
And you can also use multiple in-clauses with the every
expression, just as you can with some
, as in this example:
every $operand1 in (4, 5, 6), $operand2 in (7, 8, 9) satisfies $operand1 * $operand2 > 27
You can see an example where we're using every
in an XSLT 2.0 stylesheet in ch08_04.xsl
(Listing 8.4). In this case, we're testing three temperatures to see if they're all above 72, and if so, we'll display the message “Too hot”.
Example 8.4. An XSLT example Using the every
Expression (ch08_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:variable name="t1" select="80" /> <xsl:variable name="t2" select="90" /> <xsl:variable name="t3" select="100" /> <xsl:template match="/"> <xsl:value-of select="if (every $temperature in ($t1, $t2, $t3) satisfies $temperature > 72) then 'Too hot' else 'OK'"/> </xsl:template> </xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?> Too hot
XPath 2.0 emphasizes data types, and you can create XPath 2.0 expressions that work expressly with types using these keywords:
instance of
—. Check an item's data type (works with any simple or complex type).
cast
—. Change an item's data type.
castable
—. Check whether an item's data type may be cast.
treat
—. Treat an item as if it were a new data type for the purposes of evaluation (but don't actually change the item's data type). (The treat
expression works with any simple or complex type.)
We'll take a look at how these expressions work now.
The instance of
operator is a Boolean operator that lets you test the type of an operand. Here's how you use it in an expression, where operand2
is a simple or complex type:
operand1 instance of operand2
This expression returns true
if operand1
is of the data type operand2
, and false
otherwise. The type you give in operand2
can be fairly general—it can be an atomic type like xs:integer
, a kind test like element()
or node()
, the keyword empty
, or an empty sequence like this: ()
. It can also be the xdt:anyAtomicType
type, which, as noted in Chapter 7, is an abstract type that you can't create variables of directly. If you use the xdt:anyAtomicType
type with the instance of
operator, you're saying that you're testing for any of the atomic types.
Using the instance of
expression, you can test the data type of various items; for example, this expression returns true
because 26 is an integer:
26 instance of xs:integer
Similarly, if $variable
contains an integer value, this expression will return true
:
$variable instance of xs:integer
xs:integer
AGAINST THE xs:decimal
TYPEActually, testing an xs:integer
value against the xs:decimal
type will return true
because in XML schemas, the xs:integer
type is derived by restriction from xs:decimal
.
Here's an example that checks if 3.1415 is of type xs:decimal
:
3.1415 instance of xs:decimal
Here's an example that lets you test the context item to see if it's an element:
. instance of element()
Instance of
is very useful when you are working with schema-validated nodes and need to examine their runtime type.
Because XPath 2.0 implements strong typing, it's sometimes necessary to convert values from one data type to another. You can use a cast
expression to change an item's data type to another data type. You can use a cast
expression like this:
source cast as target-type
Here, source
is cast to a new data type, the target-type
. In this case, the data type of source
is actually changed to the target-type
, if that cast is possible.
Bear in mind that you can also use the constructor functions that come with various types to cast data from one type to another. For example, to cast an xs:string
into an xs:date
, you can use the xs:date
constructor function to create a new xs:date
value like this: xs:date("2005-03-02")
.
Here are a few examples showing how to use cast
:
"2004-09-02" cast as xs:date $variable cast as xs:integer $temperature cast as xs:decimal
When can you use cast
? Here's the list:
cast
expressions are supported for the combinations of input type and target type listed in the table at http://www.w3.org/TR/xquery-operators/#casting. This is the definitive place to look for cast
operations. For example, you can find there that you can cast from xs:double
to xs:decimal
.
cast
expressions are supported if the input type is a derived atomic type and the target type is a supertype of the input type. For example, if the zipcode
type is derived by restriction from xs:integer
, a value of type zipcode
can be cast into the type xs:integer
.
cast
expressions are supported if the target type is a derived atomic type and the input type is xs:string
or xdt:untypedAtomic
.
cast
expressions are supported if the target type is a derived atomic type and the input type is a supertype of the target type. The resulting value is the same as the input value, but with a different type.
These rules can get pretty involved—how can you be sure the cast you're about to attempt is legal? Luckily, there's a fairly easy answer to that—you can use a castable
expression to check if a cast is legal.
The castable
expression lets you test if an item may be cast to a specific type, and it returns a true
/false
answer. Here's how you use this expression:
source castable as target-type
This expression is true if source
may be cast to target-type
. Here's an example where we're checking whether $fruit
may be cast to the type apple
, and if not, whether it can be cast to the type orange
:
if ($fruit castable as apple) then $fruit cast as apple else if ($fruit castable as orange) then $fruit cast as orange else $fruit cast as xs:string
The treat
expression is much like the cast
expression, except that, unlike cast
, it doesn't change the type of its operand. It acts like an assertion, checking the type of an expression. Here's how you use the treat
operator in an expression:
source treat as target-type
This expression asserts that source
is of the target-type
data type. Otherwise, this expression returns an error.
Here's an example. In this case, the original type of $number
might be number
. Say that another type, ZIP
, is derived from that type; in that case, this expression will be of the ZIP
type when evaluated:
$number treat as ZIP
This expression also succeeds if the type of $number
is ZIP
. In other words, the treat
expression acts much like an assertion, which succeeds if $number
is of type ZIP
or of a type derived from ZIP
.
Primary expressions can be a literal, a variable, a function call, a context item, a comment, or a parenthesized expression.
Besides the primary expressions, you can also use the arithmetic operators +
, -
, *
, div
, idiv
, and mod
. These operators perform addition, subtraction, multiplication, division, integer division, and modulus, respectively.
You can also create path expressions in XPath 2.0. XPath 2.0 path expressions are much the same as they are in XPath 1.0, with some exceptions, such as the deprecation of the namespace axis, and some additions, such as new node tests.
You can also create sequence expressions in XPath 2.0 that return sequences using the comma operator, and combine sequences with the union
, intersect
, and except
operators.
XPath 2.0 also supports a number of comparison operators: eq
, ne
, lt
, le
, gt
, and ge
for comparisons of single values, and =
, !=
, <
, <=
, >
, and >=
to work with sequences. Node comparisons with is
let you compare nodes. And the order comparisons <<
and >>
let you determine the order of nodes.
XPath 2.0 logical expressions are supported with the and
and or
logical operators. These operators let you connect logical operands.
The for
expression lets you create a loop to iterate over your data. This expression works much like the for
statement in various programming languages, and supports the use of variables. The if
expression lets you create conditional statements that let you test an expression's value and branch accordingly.
The quantified expressions, some
and every
, let you perform tests on the items in a sequence. The some
expression tests whether at least one item in a sequence or sequences satisfies a particular expression, and the every
expression tests whether every item in the sequence or sequences satisfies a test expression.
XPath 2.0 also contains expressions that deal with types—instance of
to check an item's type, cast
to change an item's data type, castable
to check whether an item may be cast to a particular new type, and treat
to treat an item as if it were of a different data type without actually changing the item's data type.
3.134.78.106