Using XML Queries and Transformations

Selecting Subsets

Now we have seen most of the basic elements of building XPaths. There is only one more to discuss: predicates. Predicates are a way to select a subset from a result set in an XPath (or part of an XPath). An XPath with a predicate looks like this:

<axis>:<node-test>[<predicate expression>]

We have already seen the axis and node test. Now the predicate expression gets appended in square brackets. Basically, what the predicate does is place a filter on the result set. For each node in the set, the XPath processor will test the predicate expression.

The Expression is True/False

If the expression evaluates to true, the node remains in the result set; if it evaluates to false, the node is removed. The predicate can contain special XPath functions (we will see those later, although we already met with text(), comment() etc.), numeric values and XPath expressions. This XPath expression would return the second child element named chapter from the context node.

child::chapter[position() < 2]

The position() function returns the position of the context node in its set. The set is the result of the node test child::chapter. For the first node in the set, position() will return 1, for the second 2, etc. The expression position()<2 evaluates to true only for the first and second chapter elements found.

The Expression Returns a Number

If the expression evaluates to a numerical value n, it is only true for the nth node. If the value is 2, only the second node in the set will remain in the set, the rest will be deleted. The next example will return only the first chapter element found among the children of the context node.

child::chapter[1]

The number can also be the result of a calculation. The last() function returns the number of nodes in the result set of the current context node. Using this numeric value we can select the last chapter:

child::chapter[last()]

The Expression Returns a Node Set

If the result of the expression is a node set, the context node is included if there are nodes in the node set. The context node is deleted if the returned node set is empty. The expression can itself be an XPath expression (with axes, node tests and predicates). The inner XPath is evaluated with the outer XPath result as its context. This is a powerful concept; it allows us to make sub-querying constructions. The next example selects only those chapter elements that have para elements among their children:

child::chapter[child::para]

The outer XPath expression selects all chapter elements from the children of the context node. Then, taking each of these chapter elements as context, it tries to select para elements from their children. The chapters that have an empty set of results are removed from the result set of the outer XPath expression.

This query selects all messages that are a descendant of the context query and have an ID attribute. Note that the results of this query are the message elements, not the ID attributes:

descendant::message[attribute::ID]

Here a node, the attribute confidentiality, is compared with a literal string value:

descendant::message[attribute::confidentiality='secret']

In these cases, XPath compares the string value of the node with the literal string value. If they are identical, the expression is true. If the literal is numerical, the string value of the node is converted to a numerical value and then compared. If a node set is compared with a literal value, the expression is true if one of the elements in the set is identical to the literal value. If two node sets are compared, the result is true if any one node from the first can be matched with any one node from the second.

So in the example above, the predicate is true, if the context node has a confidentiality attribute with value 'secret'. Only if this is the case will the message will be selected.

Note that with this form of comparing, these two expressions are not identical:

descendant::*[attribute::*='Teun'] descendant::*[not(attribute::*!='Teun')]

The first query selects all descendants that have an attribute with value 'Teun'. The second one selects only descendants with allattributes set to 'Teun' (!= means 'not equal to'). If you don't immediately understand this, try to figure out when this query evaluates to true:

descendant::*[attribute::*!='Teun']

It selects all descendants that have an attribute that does not have the value 'Teun'. The reverse of this is selecting all descendants that have no attribute that does not have the value 'Teun', which is identical to selecting only descendants that have all attributes set to the value 'Teun'. In expressions like these, you can use the following operators:

=

Equal to.

!=

Not equal to.

<, <=, >, >=

Less than, less than or equal to, greater than, greater than or equal to.

and, or

Logical and, or.

+, -, *

Addition, subtraction, multiplication. Because can be part of a valid name and * can be used to indicate an arbitrary name, you have to make sure they cannot be interpreted wrongly by leaving whitespace before the operator.

div

Division (floating point).

mod

Integer remainder of a division.

|

Union of two node sets (creates a new node set holding all elements in the two node sets).

The filtered result set returned by an XPath expression with a predicate expression can be further filtered by appending another predicate to it. This example selects the fifth employee that has a function child element with the value 'manager'. We first select all employee nodes along the descendant axis, and then filter them with the [function='manager'] predicate. From this filtered result set, we again filter only the fifth element with the predicate [5]:

descendant::employee[function='manager'][5]

The following example looks very much the same, but selects the fifth employee element, but only if it has a child element of type function with the value 'manager'. Otherwise it will return an empty node set:

descendant::employee[5][function='manager']

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“There's no test like production” - Anon