The .NET Platform
Development Tools
COM & COM+
Data Access
Web Development
XML Technologies
Windows Servers
Wireless & Mobile
Security issues
Design & Process
Career Development
Analysis & Comment
Disposable Objects
You are not logged in: login here to access all areas.
XPath is the equivalent of SQL for the XML world. Martin Gudgin of DevelopMentor demonstrates how it allows you to query XML documents, and how to use it from MSXML.
Author: Martin Gudgin
Last updated: Jul 2001
The XML Information Set (Infoset) defines an abstract data model for XML. This data model is broadly tree-based and models the structure and data found in XML documents: elements, attributes, character data, processing instructions and comments. What the Infoset does not provide is a way to define subsets of the nodes in a tree, which is where the XML Path Language (XPath) comes into the picture. Location Paths Node-tests and axes axis::node-test[predicates] The location paths so far have been made up entirely of node-tests; an axis has not been specified and no predicates have been used. The node-tests used so far have been name based. These test for elements with particular names. Each node in the axis is checked to see if it matches the node-test. If the name matches, then the node becomes part of the resulting node-set for that location step. Name based node-tests can also specify a wildcard using the asterisk character. This matches any name. So any element that is a child of pets could be selected by the location path /pets/*. This location path would select both the dogs element and the cats element. /pets/cats/cat/colour/text() will select the three text nodes that are children of the colour elements. /pets/cats/cat/colour/text() can be rewritten as /child::pets/child::cats/child::cat/ This latter form is known as the verbose form, while the former was in the abbreviated form. The axis identifier defines the set of nodes that will form part of the resulting node-set; the node-test filters this set. For example the axis identifier in the expression /descendant::text() broadly scopes the resulting node-set to descendants of the root node. This covers all nodes in the tree apart from attribute and namespace nodes. The node-test then selects only those nodes which are text nodes; the expression returns all the text nodes in the tree - five in this case. /descendant::cat/attribute::breed would return a node-set containing three attribute nodes, one for each of the breed attributes associated with the three cat elements in the document. Thus the attribute axis supports node tests by name, just as all other axes do. However, the attribute axis only matches attribute nodes while for all other axes, apart from the namespace axis, node-tests by name always match element nodes. In order to match other node types in axes such as descendant or following, a type based node test must be used. Note that while the axis identifier provides a scope for matches, it is the node-test which determines what nodes are actually part of the result set and it is the node-test, in the final location step, that determines what kind of nodes are the ultimate result of evaluating the expression. Thus the location path /pets/cats/cat/colour/text() will always return text nodes while /pets/dogs/dog/colour will always return colour element nodes. Predicates /descendant::cat[attribute::breed =
'Siamese'] The predicate in this case is attribute::breed =
'Siamese' and will evaluate to true if the normalised value of the breed attribute on a cat element is the string
'Siamese'. This will only evaluate to true for one cat element, so the expression returns exactly one node. /descendant::cat/attribute::breed[ which would always return breed attribute nodes whose normalised value was
'Siamese'. /descendant::dog[child::colour/ will select the dog element whose colour element child has a text node child whose value is equal to
'Golden'. Note that predicates can include XPath location paths -
child::colour/child::text(),
in this example. These location paths could also contain predicates! Comparisons can also be done against floating point numbers rather than strings. Multiple predicates can be specified and are seperated by an
'and' or 'or' operator; for example, /descendant::cat[attribute::breed =
'Siamese' and Generally speaking, given a regular tree, as a location path is evaluated from left to right the number of nodes in the resulting node-set increases at each step. Predicates serve to narrow that result step. Predicates should be specified as early as possible in the XPath expression in order to reduce the number of nodes that must be checked in subsequent location steps. /descendant::cats/child::cat[position() = 2] will return the second cat element child of the cats element. Similarly, /descendant::cats/child::cat[position() = last()] will return the last cat element - the third in this case. /child::pets/*[count( child::* ) > 2] This location path selects any child element of pets that has more than two child elements of its own; in this case this would return the cats element. The XPath expression used as the argument to the count function can itself contain predicates. Note that the child, descendant, descendant-or-self, following and following-sibling axes are all forward axes; the nodes are returned in document order (document order in XML is a depth-first traversal of the tree). Conversely, the ancestor, preceding and preceding-sibling axes are all reverse axes - the nodes are returned in reverse document order. /descendant::cat[contains( attribute::breed,
'ese' )] would return all cat element nodes whose breed attribute contained the string
'ese'. The substring functions can similarly be used to pull out any part of a text node or attribute value. Note that all the string functions in XPath are case sensitive. /descendant::cat[not(contains( attribute::breed,
'ese' ))] would select all cat elements that do not have the string 'ese' in their breed attribute value. Concise and flexible Useful sites
XPath is, as its name suggests, a path based syntax that is used to define subsets of XML data trees. A given expression in XPath describes a set of nodes in an XML tree. Evaluating the expression produces those nodes as the result. In short, XPath is a query language for XML, allowing various nodes in a tree to be pulled out according to certain criteria. Such criteria include parent/child relationships with other nodes in the tree, nodes with particular textual or numeric values and elements having particular attributes attached to them.
XPath is a useful tool in its own right in any XML developer's toolbox, allowing developers to describe subsets of, and relationships between, nodes in a fashion that is independent of programming language while having the underlying XML processor do all the hard work. This saves developers from having to write reams of DOM traversal code or complex SAX ContentHandler implementations. In addition, other XML technologies such as Extensible Stylesheet Language Transformations (XSLT) and XML Schema build on the XPath language, putting it to use in their respective technologies.
These are the basic syntactic constructs in XPath. Each location path describes the path from an initial start point to a set of nodes in the XML tree. The result of evaluating a location path is always a set of nodes, known as a node-set. For example, given the XML file shown in
figure 1 and the corresponding tree of nodes, the location path /pets/cats would evaluate to a node-set containing a single node: the cats element. Similarly, the location path /pets/cats/cat would evaluate to a node-set containing all three cat elements. It is worth noting that describing the path to a set of nodes effectively performs some level of validation on the tree; it ensures that the tree contains at least the nodes identified in the XPath expression. This can be useful where Document Type Definition (DTD) or Schema based validation is undesirable. However, while a successful evaluation of an XPath expression ensures that the document contains at least the specified nodes, it does not say anything about extra, possibly unwanted, nodes that may be in the tree.
Both of the location path examples shown so far are made up of several parts, called location steps. Each location step is separated from the previous step by a forward slash. Thus the first example is made up of two location steps while the second example has three. Location paths can be either absolute or relative. Both the examples shown are absolute location paths as denoted by the initial forward slash. Absolute location paths always start at the root node of the tree while relative location paths are relative to some context, a node or set of nodes in the tree. For example, if the current node was the pets element then the location path cats/cat would return the same node-set as the preceding example.
Each location step in a location path is evaluated relative to a context. An XPath context is the set of nodes in the tree that were the node-set result of the previous location step. Strictly speaking, the context in XPath is made up of several properties based on a set of nodes. These properties include the size of the node-set, the position of the current node in that set and the current node itself. Each location step is evaluated relative to each node in the previous node-set. Location paths are evaluated left to right relative to an initial context.
Taking the /pets/cats/cat example above, the initial context is the root node because the location path is absolute. The pets location step is evaluated relative to the root, and matches against a pets element which is a child of the root. There is such a node in the tree, so that element is the result of evaluating the pets location step. The pets element then becomes the context for evaluating the second location step, cats, which matches against a child cats element. Again such a node exists and becomes the context for the third location step, cat. This matches against cat element children and there are three such nodes in the tree. The result of this final location step is a node-set containing three cat elements. Note that the context changes every time a forward slash is encountered and that the resulting node-set, of the location step to the left of the forward slash, becomes the context for the location step to the right of the forward slash.
Location steps are actually made up of several parts: an axis identifier, a node-test and zero or more predicates, using the syntax
In addition to performing node tests by name, node tests by type are also supported. XPath supports several node types: node(), text(), comment() and processing-instruction(), which match any node, text nodes, comment nodes and processing instruction nodes respectively. Thus the location path
The axis identifier is separated from the node-test by two colons. There are many axes in XPath, as listed in
Figure 2. Most are reasonably simple to grasp: the child axis encompasses nodes that are immediate children of the context node, so just one level deep, while the descendant axis covers nodes below the context node at any depth. Similarly the parent axis contains just one node, the context node's parent, while the ancestor axis contains all the ancestors of the context node including the root node. The preceding and following axes are a little more difficult to understand; in fact they are easier to understand by looking at the XML serialization syntax rather than the node based tree view. With respect to elements, the following axis could be described as
'all nodes whose start tag comes after my end tag'. Conversely the preceding axis could be described as
'all nodes whose end tag comes before my start tag'. Any XML tree is partitioned by the self, ancestor, descendant, preceding and following axes.
The default axis is the child axis, thus the text node selection example
child::colour/child::text()
The attribute axis is used whenever attributes need to be selected. For example the location path
In addition to specifying an axis identifier and a node-test, predicates can also appear in location steps. A predicate is a boolean expression; it evaluates to true or false relative to the current context. If it evaluates to true then the context node stays in the resulting node-set, otherwise the node is dropped. Predicates appear in square brackets after the node test and can contain a wide variety of expressions. Attribute values can be compared to string literals, for example
Note that the XPath context changes when a predicate is evaluated; the predicate is evaluated relative to the axis identifier and node-test of the location step the predicate is part of, rather than relative to the result of the previous location step. Note also that the predicate does not change the kind of nodes that are returned. The above expression always returns cat elements; it never returns breed attributes. Contrast this with the location path
self::node() = 'Siamese']
In addition to testing against attribute values, text node children of elements can also be tested against in a predicate;
child::text() = 'Golden']
child::colour/child::text() = 'Cream'] or
/descendant::cat[attribute::breed = 'Siamese' or child::colour/child::text() =
'Grey']
XPath supports a large number of functions that can be used inside predicates. These functions are divided into four categories: node-set functions, number functions, string functions and boolean functions.
Node-set functions include: position(), which returns the position of the context node in the context node-set (note this position is 1 based ); last(), which returns the size of the context node-set; and count(), which takes an XPath expression as an argument and returns the number of nodes in the resulting node-set. For example, the location path
The count() function can be used as in this example:
Strictly speaking the self axis is a forward axis and the parent axis is a reverse axis, but as these axes contain at most one node the distinction is not relevant. Note that it is especially important to remember whether an axis is a forward or reverse axis when using the position() function in a predicate.
XPath supports a set of string functions, including starts-with(), contains(), substring-before(), substring-after(), substring() and string-length(). For example, the location path
The number functions include sum() which takes an XPath expression and adds the values of all the nodes in the result set. Of the boolean functions, the most important is not() which allows a predicate to be negated; for example,
XPath is a concise way of selecting subsets of nodes in an XML tree. It is very flexible, supporting multiple axes, several different node-tests and complex predicate expressions, and has a comprehensive function library.
Writing the equivalent DOM or SAX code would take much longer and would be much harder to maintain. XPath expressions are concise enough to be easily contained within XML documents in either element or attribute content; already XPath forms part of XSLT, XML Schema and XPointer.
Many XML processors have XPath support including the MSXML 3.0 parser, the Oracle Java DOM parser and Apache's Xerces parser (via the Xalan XSLT processor). The performance of these processors is reasonable today and can be expected to improve over time as query optimizations are built into the implementations. Possible future enhancements to XPath include a richer set of built in types, matches based on element or attribute type (with an associated XML Schema) rather than name, and support for regular expressions in string matching.
MSXML 3.0 from Microsoft: http://msdn.microsoft.com/xml/c-frame.htm?/xml/general/xmlparser.asp
Xalan processor from Apache:
http://xml.apache.org
XML Information Set Working Draft:
www.w3.org/TR/xml-infoset/
XML Path Language (XPath) 1.0 Recommendation:
www.w3.org/TR/xpath
XPath Requirements Version 2.0 Working Draft:
www.w3.org/TR/xpath20req
Click here for our Privacy Statement. Copyright © Matt Publishing. All rights reserved. No part of this site may be reproduced without the prior consent of the copyright holder.