- Much of the code in this book has involved navigating the tree structure of an XML document to find particular nodes
- XPath is a fourth generation declarative language for locating nodes in XML documents
- In the large, using XPath in a Java program is like using SQL in a Java program
- XPath can be thought of as a query language like SQL
- Here are some XPath expressions that identify particular parts of this document:
- /weather/report is an XPath expression that selects the two report elements.
- /weather/report[1] is an XPath expression that selects the first report element.
- /weather/report/temperature is an XPath expression that selects the two temperature elements.
- /weather/report[locality="Santa Monica"] is an XPath expression that selects the second report element.
- //report[locality="Block Island"]/attribute::longitude is an XPath expression that selects the longitude attribute of the first report element.
- /child::weather/child::report/child::wind/child::* is an XPath expression that selects all the direction, speed, and gust elements.
- 9 * number(/weather/report[locality="Block Island"]/temperature) div 5 + 32 is an XPath expression that returns the temperature on Block Island in degrees Fahrenheit.
- /descendant::* is an XPath expression that selects all the elements in the document.
- Like SQL, XPath expressions are used in many different contexts including:
- Dedicated query tools like Alex Chaffee’s XPath Explorer
- Native XML databases like the Apache XML Project’s XIndice and Software AG’s Tamino.
- As a component of other, broader languages like XSLT and XQuery.
- And last and certainly not least, as a search component for your own Java programs that read XML documents.
- An XPath query operates on a namespace well-formed XML document after it has been parsed into a tree structure
- The document itself
- An element
- An attribute other than one that declares a namespace
- The maximum uninterrupted run of text between tags, comments, and processing instructions
- A comment
- A processing instruction
- A namespace mapping in scope on an element
- The XPath data model does not include entity references, CDATA sections, or the document type declaration
- In the XPath data model each node has a string-value Furthermore, attributes, elements, processing instructions, and namespace nodes have expanded names, which are divided into a local part and a namespace URI
- If an XPath function such as local-name() or namespace-uri() attempts to retrieve the value of one of these properties for a node that doesn’t have that property, it returns the empty string.
- An diagram and example should help explain this
- is a UML object diagram that identifies the connections between and properties of the different XPath nodes in this document
- The XPath data model is similar to, but not quite the same as the DOM data model
- Other differences between the DOM and XPath data models include:
- XPath does not have separate nodes for CDATA sections
- XPath does not include any representation of the document type declaration.
- Each XPath text node always contains the maximum contiguous run of text
- All entity references must be resolved before an XPath data model can be built
- In XPath, the element that contains an attribute is the parent of that attribute, although the attribute is not a child of the element.
- Every namespace which has scope for an element or attribute is an XPath namespace node for that element or attribute
- Although there are many different kinds of XPath expressions, the one that’s of primary use in Java programs is the location path
- Some examples will help explain all these terms
- Exactly how the context node for a location step is determined depends on the environment in which the location step appears
- Location paths are not guaranteed to return a node-set that contains exactly one node (and assuming they do is a very common mistake)
- There are twelve axes along which a location step can move
- The node itself.
- All child nodes of the context node
- All nodes completely contained inside the context node (between the end of its start-tag and the beginning of its end-tag); that is, all child nodes, plus all children of the child nodes, and all children of the children’s children, and so forth
- All descendants of the context node and the context node itself.
- The node which most immediately contains the context node
- The root node and all element nodes that contain the context node.
- All ancestors of the context node and the context node itself.
- All non-attribute, non-namespace nodes which come before the context node in document order and which are not ancestors of the context node
- All non-attribute, non-namespace nodes which come before the context node in document order and have the same parent node
- All non-attribute, non-namespace nodes which follow the context node in document order and which are not descendants of the context node.
- All non-attribute, non-namespace nodes which follow the context node in document order and have the same parent node
- Attributes of the context node
- Namespaces in scope on the context node
- For example, consider the slightly more complex SOAP request document in
- The self axis contains one node, the middle Quote element that was chosen as the context node.
- The child axis contains three nodes: a text node containing white space, an element node with the local name Price, and another text node containing only white space, in that order
- The descendant axis contains four nodes: a text node containing white space, an element node with the local name Price, a text node with the value "24.85", and another text node containing only white space, in that order.
- The descendant-or-self axis contains five nodes: an element node with the local name Quote, a text node containing white space, an element node with the local name Price, a text node with the value "24.85", and another text node containing only white space, in that order.
- The parent axis contains a single element node with the local name Body.
- The ancestor axis contains three nodes: an element node with the local name Body, an element node with the local name Envelope, and the root node in that order.
- The ancestor-or-self axis contains four nodes: an element node with the local name Quote, an element node with the local name Body, an element node with the local name Envelope, and the root node in that order.
- The preceding axis contains eight nodes: a text node containing only white space, another text node containing only white space, a text node containing the string 7.02, an element node named Price, another text node containing only white space, an element node named Quote, a text node containing only white space, a comment node in that order
- The preceding-sibling axis contains three nodes: a text node containing white space, an element node with the name Quote and the symbol RHAT, and another text node containing only white space.
- The following axis contains eight nodes: a text node containing only white space, a Quote element node, a text node containing only white space, a Price element node, a text node containing the string 68.59, and three text nodes containing only white space
- The following-sibling axis contains three nodes: a text node containing white space, an element node with the name Quote and the symbol BAC, and another text node containing only white space.
- The attribute axis contains one attribute node with the name symbol and the value AAPL.
- The namespace axis contains two namespace nodes, one with the name SOAP-ENV and the value http://schemas.xmlsoap.org/soap/envelope/ and the other with an empty string name and the value http://namespaces.cafeconleche.org/xmljava/ch2/.
- Generally these sets would be further subsetted via a node test
- The axis chooses the direction to move from the context node
- Any element or attribute with the specified name
- Along the attribute axis the asterisk matches all attribute nodes
- Any element or attribute in the namespace mapped to the prefix.
- Any comment
- Any text node
- Any node
- Any processing instruction
- Any processing instruction with the specified target
- For example, once again considering the SOAP request document in and choosing the AAPL Quote element as the context node, consider these location steps:
- self::* selects one node, the middle Quote element that serves as the context node.
- child::* selects one node, an element node with the name Price and the value 24.85.
- child::Price selects no nodes because there are no Price elements in this document that are not in any namespace.
- child::stk:Price selects one node, an element node with the name Price and the value 24.85, provided that the prefix stk is bound to the http://namespaces.cafeconleche.org/xmljava/ch2/ namespace URI in the local environment.
- descendant::text() selects three nodes: a text node containing white space, a text node with the value "24.85", and another text node containing only white space.
- descendant-or-self::* selects two nodes: an element node with the name Quote and an element node with the name Price.
- parent::SOAP-ENV:Envelope selects an empty node set because the parent of the context node is not SOAP-ENV:Envelope.
- ancestor::SOAP-ENV:Envelope selects one node, the document element, assuming that the local environment maps the prefix SOAP-ENV to the namespace URI http://schemas.xmlsoap.org/soap/envelope/.
- ancestor::SOAP-ENV:* selects two nodes, the SOAP-ENV:Body element and the SOAP-ENV:Envelope element, again assuming that the prefixes are properly mapped.
- ancestor-or-self::* selects three nodes: an element node with the local name Quote, an element node with the local name Body, and an element node with the local name Envelope.
- preceding::comment() selects the single comment in the prolog.
- preceding-sibling::node() selects three nodes: a text node containing white space, an element node with the name Quote and the symbol RHAT, and another text node containing only white space, in that order.
- following::* selects two nodes: a Quote element node and a Price element node.
- following-sibling::processing-instruction() returns an empty node-set.
- attribute::symbol selects the attribute node with the name symbol and the value AAPL.
- namespace::SOAP-ENV returns a node-set containing a namespace node with name SOAP-ENV and the value http://schemas.xmlsoap.org/soap/envelope/.
- namespace::* returns a node-set containing two namespace nodes, one with the name SOAP-ENV and the value http://schemas.xmlsoap.org/soap/envelope/ and the other with an empty string name and the value http://namespaces.cafeconleche.org/xmljava/ch2/.
- Each location step can have zero or more predicates that further filter the node-set
- If this XPath expression were embedded in an XML document, you might need to escape the less than sign as <
- There can be more than one predicate
- If the predicate returns a number, then the node is kept in the set only if the number is equal to the position of the context node in the context node list
- If the context node has fewer than three Quote children, then this returns an empty node-set.
- If the predicate returns a string, then the context node is deleted from the set if the string is empty and kept otherwise
- This is not quite the same as selecting the Quote elements that have a symbol attribute
- If the predicate returns a node-set, then the source node is kept in the returned set only if the predicate node-set is non-empty
- This location step finds those Quote children of the context node that have at least one Price child and at least one Quantity child:
- When applied to the SOAP-ENV:Body element in , it returns an empty node-set because none of its Quote children have a Quantity child.
- The forward slash (/) combines location steps into a location path
- Continuing with the same example in and still using the second Quote element as the context node, consider these location paths (Here I assume that the environment for the XPath expressions maps the prefix stk to the namespace URI http://namespaces.cafeconleche.org/xmljava/ch2/ and the prefix SOAP-ENV to the namespace URI http://schemas.xmlsoap.org/soap/envelope/):
- This selects the currency attribute node currency="USD"
- This selects one node, the first value element in the document.
- This selects all three Quote element nodes in the document, including the context node itself.
- This selects the AAPL and the BAC Quote element nodes, but not the RHAT Quote element node.
- This selects all three Price element nodes in the document.
- This selects the Price element node of the BAC Quote element.
- This selects all three currency attribute nodes in the document.
- So far all the location paths have been relative to a specified context node
- Continuing with the same example in and once again assuming that the environment binds the prefix stk to the namespace URI http://namespaces.cafeconleche.org/xmljava/ch2/ and the prefix SOAP-ENV to the namespace URI http://schemas.xmlsoap.org/soap/envelope/, consider these location paths:
- This selects all three Price element nodes.
- This selects the single SOAP-ENV:Body element node.
- This selects all three Price element nodes in the document.
- This selects the Quote element nodes whose Price is greater than 20; i.e
- This returns an empty node-set because the root element of the document is SOAP-ENV:Envelope, not SOAP-ENV:Body.
- This returns a node-set containing all attribute nodes in the document.
- This returns a node-set containing all non-attribute, non-namespace nodes in the document.
- This selects the root node of the document.
- XPath location paths can use the abbreviations listed in in location paths
- Using the abbreviated forms, the previous batch of relative XPaths selecting from using the second Quote element as the context node can be rewritten like this:
- This selects the currency attribute node currency="USD"
- This isn’t an exact abbreviation for preceding-sibling::stk:Quote/descendant::* (// expands to /descendant-or-self::node()/, not /descendant::) but the node-set selected is the same, the first Price element in the document.
- This selects all three Quote element nodes in the document, including the context node itself.
- This also isn’t an exact abbreviation for the original expression, but again it selects the same node-set, which in this case contains all three Price element nodes in the document.
- This selects the AAPL and the BAC Quote element nodes, but not the RHAT Quote element node.
- This selects the Price child element node of the BAC Quote element.
- This too isn’t an exact abbreviation for the original expression, but once again it selects the same node-set containing all three currency attribute nodes in the document.
- Absolute location paths can also be abbreviated
- Here are some example of abbreviated absolute location paths that apply to :
- This selects all three Quote element nodes.
- This selects the single SOAP-ENV:Body element node.
- This selects all three Price element nodes in the document.
- This selects the Quote element nodes whose Price is greater than 20; i.e
- This returns an empty node-set because the root element of the document is SOAP-ENV:Envelope, not Price.
- This returns a node-set containing all attribute nodes in the document.
- This returns a node-set containing all non-attribute, non-namespace nodes in the document.
- Occasionally it’s useful to select a node-set that’s built from multiple, more or less unrelated parts of an XML document
- selects all the Price element nodes and all the Quote element nodes in the document.
- selects all the currency attribute nodes and all the Price element nodes.
- selects all the Price and Quantity child elements of all Quote elements.
- Not all XPath expressions are location paths
- A sequence of zero or more Unicode characters
- An IEEE-754 double
- Semantically the same as Java’s boolean type
- An unordered collection of nodes from an XML document without any duplicates
- Different XPath engines map these four types to different Java classes and primitive data types
- The XPath expression syntax includes literal forms for strings and numbers as well as operators and functions for manipulating all four XPath data types.
- The primary use-case for XPath literals and operators is predicates
- XPath defines literal forms for strings and numbers
- XPath string literals are enclosed in single or double quotes
- There are no boolean or node-set literals
- XPath provides the following operators for basic floating point arithmetic:
- All five behave the same as the equivalent operators in Java
- XPath also provides these operators for comparisons and boolean logic:
- In an XML context such as an XSLT stylesheet, some of these may need to be escaped with < or >
- Additional arithmetic and boolean operations such as rounding and negation are provided by various XPath functions.
- XPath defines a number of useful functions that operate on and return the four fundamental XPath data types
- None of these functions modify their arguments in anyway
- Returns the number of nodes in the context node list
- Returns the position of the context node in the context node list
- Returns the number of nodes in the argument
- Returns a node-set containing the single element node with the specified id as determined by an ID-type attribute
- Returns the local name of the first node in the argument node-set, or the local name of the context node if the argument is omitted
- Returns the namespace name of the first node in the argument node-set, or the namespace name of the context node if the argument is omitted
- Returns the full, prefixed name of the first node in the argument node-set, or the name of the context node if the argument is omitted
- Converts the argument to a boolean in a mostly sensible way
- This function turns true into false and false into true.
- This function always returns true
- This function always returns false
- This function returns true if the context node is written in the language specified by the argument
- This function returns the string-value of the argument
- This function returns a string containing the concatenation of all its arguments.
- This function returns true if the first string starts with the second string
- This function returns true if the first string contains the second string
- This returns that part of the first string that precedes the second string
- This returns that part of the first string that follows the second string
- This returns the substring of the first argument beginning at the second argument and continuing for the number of characters specified by the third argument (or until the end of the string if the third argument is omitted.)
- Returns the number of Unicode characters in the string, or the string-value of the context node if the argument is omitted
- This function strips all leading and trailing white-space from its argument, or the string-value of the context node if the argument is omitted, and condenses all other runs of whitespace to a single space
- This function replaces all characters in the first string that are found in the second string with the corresponding character from the third string.
- This function converts its argument to a number in a reasonable way
- Each node in the node-set is converted to a number, as if by the number() function
- Returns the largest integer less than or equal to the argument.
- Returns the smallest integer greater than or equal to the argument.
- Returns the integer nearest to the argument.
- There’s more to XPath than the basics I’ve covered here
- There are several good open source XPath engines for Java, most distributed as part of XSLT processors
- A very fast XSLT processor written by Michael Kay
- An XSLT processor used by several Apache XML projects including Cocoon
- A stand-alone XPath implementation that works with DOM, JDOM, dom4j, and ElectricXML.
- Unfortunately, although the XPath data model and expression syntax are standardized, the API for integrating them into your Java programs is not
- To demonstrate the different APIs, let’s revisit the Fibonacci SOAP client from
- The server responds with a list of Fibonacci numbers enclosed in a SOAP response envelope
- The client needs to find all the fibonacci elements
- However, there’s a catch
- Having assigned it a prefix, you then have to map that prefix to a namespace URI
- The second of these two location paths is more efficient in general
- The Saxon 6.5 API is rather convoluted and involves over 200 different classes in 18 different packages
- Use JAXP to a build a Saxon Document object
- Attach the document to a Context object.
- Declare the namespaces used in the XPath expressions in a StandaloneContext.
- Make an Expression from the StandaloneContext and the string form of your XPath expression.
- Evaluate the Expression to return one of the four XPath data types.
- Saxon requires a custom DOM that has been annotated with the information it needs
- Once you’ve set the DocumentBuilderFactory, parse the input document as normal to produce a DOM Node or Document object
- You’ll notice that Saxon freely mixes classes from SAX, DOM, TrAX, JAXP, and its internal implementation
- Before this document can be searched, you’ll need to establish it as an XPath context node
- Here the root node is the context node
- The document we’ve just parsed defines its own namespace prefixes and URIs
- That does it for the preliminaries
- The NodeEnumeration class is modeled after (but does not extend) the Enumeration interface in the java.util package
- NodeEnumeration is limited to a single use
- Alternately, if you want the expression to return a number, string, or boolean, you can call one of these three methods instead:
- If the expression returns the wrong type, then Saxon will convert the result as if by the XPath number(), string(), or boolean() function
- I can now show you the complete method that takes as an argument the InputStream from which the response document will be read and searches out the relevant parts with XPath:
- Honestly, this is a little convoluted and perhaps more complex than the pure DOM, JDOM, or SAX equivalent
- The Xalan-J XSLT processor from the Apache XML Project also includes an XPath API that’s useful for navigation in DOM programs
- Each method in this class takes two or three arguments:
- The context node as a DOM Node object
- The XPath expression as a String
- The namespace prefix mappings as a DOM Node object or a Xalan PrefixResolver object
- The methods differ primarily in return type
- A single DOM Node
- A DOM NodeList
- A DOM traversal NodeIterator
- A Xalan XObject
- The first three types you’ve encountered in previous chapters
- The XObject type is new
- The last two methods only work if the XObject returned by the eval() method is in fact a node-set
- For the sake of comparison, let’s look at how we’d use these classes to solve the Fibonacci SOAP client problem addressed earlier by Saxon.
- The input document is parsed in the usual way with the JAXP DocumentBuilder class
- The XPath expression, the input document, and the context node (the root element here) are passed to XPathAPI.selectNodeList() to find all the matching elements
- One crucial difference you’ll note between Xalan and Saxon is that at no point does Xalan require or use any specific classes from the DOM implementation
- The Saxon API only works with Saxon
- This section is based on the March 28, 2002, working draft of the Document Object Model (DOM) Level 3 XPath Specification
- The XPath module has two main interfaces, XPathEvaluator and XPathResult
- In DOM implementations that support XPath, the same classes that implement org.w3c.dom.Document implement XPathEvaluator
- A client for this program needs to extract the content of the double element
- /methodResponse/params/param/value/double
- /child::methodResponse/child::params/child::param/child::value/child::double[1]
- /methodResponse/params/param/value/double[1]
- //double[1]
- /descendant::double[1]
- Depending on what you intend to do with the node once you have it, you might want to use one of the functions that returns the string-value of the node instead
- normalize-space(/methodResponse/params/param/value/double)
- normalize-space(//double[1])
- string(//double)
- normalize-space(/methodResponse)
- normalize-space(/)
- These are all absolute expressions that do not depend on the context node
- params/param/value/double
- child::params/child::param/child::value/child::double[1]
- .//double
- normalize-space(.//double[1])
- normalize-space(params)
- normalize-space(/)
- Assuming the relevant server response has already been parsed into a DOM Document object named response, the following code will extract the desired element into an XPathResult object:
- What this builds is an XPathResult object, which is one step removed from the string you actually want
- Of the four getXXXValue() methods, only one of them will actually return a sensible result for any given XPath expression
- That this expression returns a single value is indicated by foreknowledge of the input format, not by anything intrinsic to the XPath expression
- Now we have an Element node, but what we really need is the complete text of that node, after accounting for possible if unlikely comments, CDATA sections, processing instructions, and other detritus that DOM presents us with
- Or maybe it’s not a String you want but a number
- The DOM 3 XPath methods getStringValue(), getNumberValue(), and getBooleanValue() correspond to the XPath casting functions string(), number(), and boolean()
- In the case of an XPath expression that evaluates to a node-set, getSingleNodeValue() only returns the first node in the set
- Because the SOAP request document uses namespace qualified elements, however, we’ll first have to provide some namespace bindings that can be used when evaluating the XPath expression
- Now we’re ready to repeat the earlier example, but this time using the DOM XPath API instead of the processor specific Xalan or Saxon APIs
- The program both reads the XPath expression from search attribute of the document element and uses that element for the namespace bindings
- Here’s the configurable, DOM-XPath based readResponse() method
- Iterators like this one are only good for a single pass
- To hold on to a more stable list that can be reused and survives document edits, request a snapshot of the node-set returned rather than an iterator
- Of course, snapshots have the opposite problem: there is no guarantee that the nodes in the snapshot reflect the current state of the Document.
- An XPath engine that implements the DOM XPath API may need to compile the expression into some internal form rather than simply keeping it as a generic String
- You can use the createExpression() method in the XPathEvaluator interface to compile a String into an XPathExpression:
- Then you can repeatedly invoke the same expression on different documents without having to compile the XPath expression from a string each time
- This isn’t very important for an expression that’s only going to be used once or twice
- The Jaxen Java XPath Engine is an open source cross-API (DOM, JDOM, dom4j, and ElectricXML) XPath library for Java
- Jaxen’s class library is quite large, on a par with Saxon’s or Xalan’s
- The following steps search an XML document with Jaxen:
- Construct an XPath object by passing a String containing an XPath expression to the model-specific constructor:
- Set the namespace bindings by calling addNamespace() for each namespace binding the XPath expression uses:
- (You can skip this step if the XPath expression doesn’t use any prefixed names.)
- Invoke one of the following methods to evaluate the expression, depending on what type of result you expect or want:
- For an example, let’s rewrite the Fibonacci XML-RPC client one last time
- As usual, first JAXP reads the document from the InputStream
- After the expression is created, I immediately bind all the namespace prefixes it uses by invoking the addNamespace() method
- Jaxen’s selectNodes() method returns a standard java.util.List which can be iterated through in the usual way
- As the program iterates through the list, it deals with each node independently
- This is a little more convoluted than it perhaps needs to be because there’s no XPath 1.0 way to return a list of strings
- In fact, Jaxen’s org.jaxen.function package provides Java representations of most of the functions in XPath 1.0: BooleanFunction, CeilingFunction, ConcatFunction, etc
- XPath is a straightforward declarative language for selecting particular subsets of nodes from an XML document
- XPath location paths are composed of one or more location steps
- A location path is actually just one kind of the more generic XPath expressions
- Most XSLT processors have APIs that allow you to search XML documents with XPath expressions
- DOM Level 3 XPath is a developing standard for using XPath in DOM programs that can be implemented across different processors, though as yet it isn’t implemented by any
- Jaxen is a somewhat more ambitious cross-model effort that attempts to model XPath expressions themselves rather than just treating them as opaque strings