Howardism Musings from my Awakening Dementia
My collected thoughts flamed by hubris
Home PageSend Comment

Standard XPath API for Java

Oversight? I suppose. But XPath has long been a standard way of querying XML documents and is an integral part of XSL, however, for all of the interface work for having a standard way to parse XML documents from Java (without worrying about the actual XML parser available), there hasn't been a standard way to issue XPath expressions without doing a parser-specific implementation.

A Brief History of XPath and Java

I guess a little history may be in order. Once upon a time, Sun and the JCP community decided that given all of the competing implementations of APIs that parse XML documents, there should be a standard interface to all of these disparaging parser variations.

So, in order to parse an XML document into a DOM, you first instantiate an interface into a parser (and it doesn't matter which one), via the factory pattern, as in this code example:

DocumentBuilderFactory docfactory =
           DocumentBuilderFactory.newInstance();
DocumentBuilder docbuilder = 
           docfactory.newDocumentBuilder();

With a DocumentBuilder available, you can parse an XML document supplied via an java.io.InputStream like:

Document docroot = docbuilder.parse(filestream);

However, traversing a DOM using the getter methods of the Document/Node API is cumbersome at best. Often, what you want, is to jump to a particular node. Well, that is what XPath does. It allows you to specify an "expression" that specifies one or more nodes, and the result is either a Node or NodeList.

However, there is not standard Java way for doing this. Keep in mind, Standard Java does specify how to transform a document using XSL, and XSL requires XPath support to work, but there is not Java API for it. Each XML parser has supplied their own variations on this theme.

For instance, Oracle's XDK (xmlparserv2) has the following interface:

Node nodeResult = 
       docroot.selectSingleNode( xpathExpression );
// Or to get multiple nodes:
NodeList nodeResults = 
       docroot.selectNodes( xpathExpression );

Apache's Xalan/Xerces XML parser, on the other hand does this:

import org.apache.xpath.XPathAPI;

Node nodeResults = 
      selectSingleNode ( docroot, xpathExpression );
// Or to get multiple nodes:
NodeList nodeResults = 
      selectNodeList ( docroot, xpathExpression );
// It also has a node iterator:
NodeIterator nodeResultSet = 
      selectNodeIterator ( docroot, xpathExpression );

Yes, the are both similar, but unique enough that you have to code for a particular library. Not a great choice given the goals of creating the general Java-to-XML interface.

XPath and Java 5

The new JAXP API associated with Java 5 now has added an XPath interface, and I'm sure that all of the XML parsers will soon be compliant … assuming you've upgraded to this version of the JDK. According to the online API documentation, all you need to do is:

XPath xpath = XPathFactory.newInstance().newXPath();
Node resultNode = 
   (Node) xpath.evaluate (xpathExpression, docroot,
                          XPathConstants.NODE);

The two most fascinating parts of this new change is the fact that instead of having two (or more) separate methods for getting a single node or a multiple nodes, you pass in a XPathConstants value to specify what you want.

The nifty-swell aspect of this, is that instead of just asking for one or more nodes, you can now ask for a boolean, numeric value and a string… and the results is what you would expect.

If you ask for a boolean, the boolean that is returned is true if the XPath expression match something or not. The numeric value tries to coerce the results into a double. The string is the value of of the node (which for everything but an Element, turns out to be a call to Node.getValue(). However, if the results is an Element, then asking for a string, gets the value of the child text nodes!

For instance, given the following XML document:

<a>
  <b>
    Howard Abrams
  </b>
</a>

If you were to use the XPath expression, //b to get that "b" element, you could either issue a call to getName() which would have returned "b" (not very useful, since you knew that), and a call to getValue() would have returned null.

However, with this new XPath Java API, specifying the following:

String result = 
     (String) xpath.evaluate("//b", docroot,
                             XPathConstants.STRING);

Would have returned "Howard Abrams" … which is what all of us non-XML-purists would have expected. Yes, it does make the work I did on my XML Query Java Bean somewhat less important.

XPath and Performance

If you were planning on getting a large amount of data from an XML document, I wouldn't suggest a series of XPath expression queries… you should probably parse it with SAX and store all the information you want. However, let me bring out two interesting points about this new API:

First, XPath expressions can be compiled into an internal format and reused. For instance:

String xexpr1 = "//b";
XPathExpression xpath1 = 
    xpathprocessor.compile ( xexpr1 );
. . .
NodeList nodes = 
   (NodeList) xpath1.evaluate ( doc, 
                             XPathConstants.NODESET);

Percompiling XPath expressions may be a huge boon now that the XPath 2.0 specification allows for large and complex expressions.

The second point I'd like to make here is that you'll notice that an XML document is always passed to the evaluate method. Essentially this means that the XPath API is stateless, and the XML document is queried from the top. This nice thing about Oracle's XPath methods, selectNodes() and selectSingleNode() is they are associated with a Node and not a top-level document.

Of course, you can always come up with an XPath expression that can refer to any subnode of any node, however, when you start to combine XML documents, but an XPath expression was oriented for a particular embedded document, this becomes much trickier.

Tell others about this article:
Click here to submit this page to Stumble It