XPath is an expression language for addressing parts of an XML
document. You can think of XPath expressions as sort of like regular
expressions for XML. They let you pull out parts of an XML document based
on patterns. In the case of XPath, the patterns are more concerned with
structural information than with character content and the values returned
may be either simple text or “live” DOM nodes. With XPath, we can query an
XML document for all of the elements with a certain name or in a certain
parent-child relationship. We can also apply fairly sophisticated tests or
predicates to the nodes, which allows us to construct
complex queries such as this one: give me all of the Animal
s with a Weight
greater than the number 400 and a
Temperament
of irritable
whose animalClass
attribute is mammal
.
The full XPath specification has many features and includes both a compact and more verbose syntax. We won’t try to cover it all here, but the basics are easy and it’s important to know them because XPath expressions are at the core of XSL transformations and other APIs that refer to parts of XML documents. The full specification does not make great bedtime reading, but can be found at http://www.w3.org/TR/xpath.
An XPath expression addresses a Node
in an XML document tree. The node may be
an element (possibly with children) like <animal>...</animal>
or it may be
a lower-level document node representing an attribute (e.g., animalClass="mammal"
), a CDATA block, or even
a comment. All of the structure of an XML document is accessible through
the XPath syntax. Once we’ve addressed the node, we can either reduce
the content to a text string (as we might with a simple text content
element like name
) or we can access
it as a proper DOM tree to further read or manipulate it.
Table 24-2 shows the most basic node-related syntax.
Table 24-2. Basic node-related syntax
Syntax | Example | Description |
---|---|---|
| All animal nodes under | |
| | All animal nodes anywhere in document.
A |
| All child nodes of inventory ( | |
| All | |
| The current node (all | |
| | The parent node ( |
Nodes are addressed with a slash-separated path based on name. For
example, /Inventory/Animal
refers to
the set of all Animal
nodes under the
Inventory
node. If we want to list
the names of all Animal
s, we would
use /Inventory/Animal/Name
. The
//
syntax matches a node anywhere in
a document, at any level of nesting, so //Name
would match the name elements of
Animal
s, FoodRecipe
s, and possibly many other elements.
We could be more specific, using //Animal/Name
to match only Name
elements whose parent is an Animal
element. The at sign (@) matches
attributes. This becomes much more useful with predicates, which we
describe next. Finally, the familiar .
and ..
notation can be used to “move” relative to a node; read on to see how
this is used.
Predicates let us apply a test to a node. Nodes that pass the test are included in the result set or used to select other nodes (child or parent) relative to them. There are many types of tests available in XPath. Table 24-3 lists a few examples.
Table 24-3. Predicates
Syntax | Example | Description |
---|---|---|
| Select the nth
element of a set. (Starts with 1 rather than 0.) For example,
select the first | |
| Match nodes with the specified
attribute value. For example, | |
| | Match nodes with a child node whose
text value is specified. For example, match the |
| Predicates may also test for inequality and numeric greater-/lesser-than value. | |
| Predicates may use logical AND and OR
to test. For example, |
Predicates can be compounded (AND’ed) using this syntax or simply by adding more predicates, like so:
//animal[@animalClass="mammal"][weight > 400]
Here, we’ve asked for animal
s
with a class attribute of "mammal"
and a weight
element containing a
number greater than 400.
We can now also see the usefulness of the ..
operator. Suppose we want to find all of
the animal
s with a foodRecipe
that uses Fruit
as an ingredient:
//animal/foodRecipe[ingredient="Fruit"]/..
The ..
means that instead of
returning the matching foodRecipe
node itself, we return its parent—the animal
element. The .
(current node) operator is useful in other
cases where we use XPath functions to manipulate values in more refined
ways. We’ll say a few words about functions next.
The XPath specification includes not only the basic node traversal and predicate syntax we’ve shown, but also the ability to invoke more open-ended functions that operate on nodes and the node context. These XPath functions cover a wide range of duties and we’ll just give a couple of examples here. The functions fall into a few general categories.
Some functions select node types other than an element. For
example, there is no special syntax for selecting an XML comment.
Instead you invoke a special method called comment()
, like
this:
/
inventory
/
comment
()
This expression returns any XML comment nodes that are children of
the inventory
element. XPath also
offers functions that duplicate all of the (compact) syntax we’ve
discussed, including methods like child()
and parent()
(corresponding
to .
and ..
).
Other functions look at the context of nodes—for example,
last()
and count()
.
/
inventory
/
animal
[
last
()]
This expression selects the last animal
child element of inventory
in the same way that [n]
selects the
nth.
//foodRecipe[count(ingredient)>2]
This expression matches all of the foodRecipe
elements with more than two
ingredients. (Cool, eh?)
Finally, there are many string-related functions. Some are useful
for simple tests, but others are really useful only in the context of
XSL, where they help out the language (in an awkward way) with basic
formatting and string manipulation. For example, the contains()
and
starts-with()
methods
can be used to look at the text values inside XML documents:
//animal[starts-with(name,"S")]
This expression matches animal
s
whose name
starts with the character
S
(e.g., Song Fang). The contains()
method, similarly, can be used to
look for a substring in text.
Now that we’ve got a taste for the syntax, let’s look at
how to use the API. The procedure is similar to that of the Java regular
expression API for strings. We use a factory to create an XPath object.
We can then either evaluate expressions with it or “compile” an
expression down to an XPathExpression
for
better performance if we’re going to use it more than once.
XPath
xpath
=
XPathFactory
.
newInstance
().
newXPath
();
InputSource
source
=
new
InputSource
(
filename
);
String
result
=
xpath
.
evaluate
(
"//animal/name"
,
source
);
// Song Fang
Here we’ve used the simplest form of the evaluate()
method,
which returns only the first match and takes the value as a string. This
method is useful for pulling simple text values from elements. However,
if we want the full set of values (e.g., the names of all the animal
s matched by this expression), we need
to return the results as a set of Node
objects instead.
The return type of (the overloaded forms of) evaluate()
is controlled by identifiers of the
XPathConstants
class.
We can get the result as one of the following: STRING
, BOOLEAN
, NUMBER
, NODE
, or NODESET
. The default is STRING
, which strips out child element tags
and returns just the text of the matching nodes. BOOLEAN
and NUMBER
are conveniences for getting primitive
types. NODE
and NODESET
return org.w3c.dom.Node
and Node
List
objects, respectively. We need the
NodeList
to get all the
values.
NodeList
elements
=
(
NodeList
)
xpath
.
evaluate
(
expression
,
inputSource
,
XPathConstants
.
NODESET
);
This simple example can be used as a command-line utility,
such as grep, for testing XPath expressions
against a file. It applies an XPath expression and then prints the
resulting elements as XML text using the same technique we used in our
PrintDOM
example. Nodes that are not
elements (e.g., attributes, comments, and so on) are simply printed with
their toString()
method, which
normally serves well enough to identify them, but you can expand the
example to your taste. Here it is:
import
org.w3c.dom.*
;
import
org.xml.sax.InputSource
;
import
javax.xml.xpath.*
;
import
javax.xml.transform.*
;
import
javax.xml.transform.dom.DOMSource
;
import
javax.xml.transform.stream.StreamResult
;
public
class
XMLGrep
{
public
static
void
printXML
(
Element
element
)
throws
TransformerException
{
Transformer
transformer
=
TransformerFactory
.
newInstance
().
newTransformer
();
transformer
.
setOutputProperty
(
OutputKeys
.
OMIT_XML_DECLARATION
,
"yes"
);
Source
source
=
new
DOMSource
(
element
);
Result
output
=
new
StreamResult
(
System
.
out
);
transformer
.
transform
(
source
,
output
);
System
.
out
.
println
();
}
public
static
void
main
(
String
[]
args
)
throws
Exception
{
if
(
args
.
length
!=
2
)
{
System
.
out
.
println
(
"usage: PrintXPath expression file.xml"
);
System
.
exit
(
1
);
}
String
expression
=
args
[
0
],
filename
=
args
[
1
];
XPath
xpath
=
XPathFactory
.
newInstance
().
newXPath
();
InputSource
inputSource
=
new
InputSource
(
filename
);
NodeList
elements
=
(
NodeList
)
xpath
.
evaluate
(
expression
,
inputSource
,
XPathConstants
.
NODESET
);
for
(
int
i
=
0
;
i
<
elements
.
getLength
();
i
++
)
if
(
elements
.
item
(
i
)
instanceof
Element
)
{
printXML
(
(
Element
)
elements
.
item
(
i
)
);
}
else
System
.
out
.
println
(
elements
.
item
(
i
)
);
}
}
There are again a lot of import
s in this example. The transform code in
our printXML()
method is drawn from
the PrintDOM
example with one
addition. We’ve set a property on the transformer to omit the standard
XML declaration that would normally be output for us at the head of our
document. Since we may print more than one (root) element, the output is
not well formed XML anyway.
Run the example by passing an XPath expression and the name of an XML file as arguments:
%
java
XMLGrep
"//animal[starts-with(name,'C')]"
zooinventory
.
xml
This example really is useful for trying out XPath. Please give it a whirl. Mastering these expressions (and learning more) will give you great power over XML documents and, again, form the basis for learning about XSL transformations.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.