JaxenThe Jaxen Java XPath Engine [http://www.jaxen.org/] is an open source, cross-API (DOM, JDOM, dom4j, and ElectricXML) XPath library for Java. Whereas DOM3 XPath attempts to be a cross-implementation, cross-language XPath API for the Document Object Model alone, Jaxen attempts to be a cross-model API for its own XPath engine. It is a Java class library that can operate on various XML object models using a standard engine rather than an API that can be offered by many different engines for one model. It allows you to pass DOM, JDOM, dom4j, and ElectricXML objects directly to XPath functions such as position() and translate() . Furthermore, whereas DOM3 XPath offers fairly rudimentary interfaces for evaluating XPath expressions in a particular document against a context node, Jaxen is a more complete object model for XPath expressions. Jaxen's class library is large, on a par with those of Saxon and Xalan. It includes classes to represent each of XPath's functions, iterators, node tests, and axes. Fortunately, you don't need to know all of them to perform simple searches. Indeed, Jaxen's basic API is probably the simplest of the XPath APIs discussed in this chapter. The main interface you need is XPath , which offers four implementations in four different packages, one each for DOM, JDOM, dom4j, and ElectricXML: org.jaxen.dom.DOMXPath , org.jaxen.jdom.JDOMXPath , org.jaxen.dom4j.Dom4j-XPath , and org.jaxen.exml.ElectricXPath . I'll demonstrate this API with the DOM implementation, but the patterns are much the same for the other three APIs. The following steps search an XML document with Jaxen:
For an example, let's rewrite the Fibonacci XML-RPC client one last time. This time, I'll use Jaxen: public static void readResponse(InputStream in) throws IOException, SAXException, TransformerException, ParserConfigurationException, JaxenException { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); DocumentBuilder builder = factory.newDocumentBuilder(); InputSource data = new InputSource(in); Node doc = builder.parse(data); // There are different XPath classes in different packages // for the different APIs Jaxen supports XPath expression = new org.jaxen.dom.DOMXPath( "/SOAP:Envelope/SOAP:Body/f:Fibonacci_Numbers/f:fibonacci"); expression.addNamespace("f", "http://namespaces.cafeconleche.org/xmljava/ch3/"); expression.addNamespace("SOAP", "http://schemas.xmlsoap.org/soap/envelope/"); Navigator navigator = expression.getNavigator(); List results = expression.selectNodes(doc); Iterator iterator = results.iterator(); while (iterator.hasNext()) { Node result = (Node) iterator.next(); String value = StringFunction.evaluate(result, navigator); System.out.println(value); } } As usual, first JAXP reads the document from the InputStream . Next a new Jaxen DOMXPath object is constructed from the String form of the XPath expression. If I were using Jaxen on top of JDOM, I would have constructed an org.jaxen.jdom.JDOMXPath object here instead. If I were using Jaxen on top of dom4j, I would have constructed an org.jaxen.dom4j.Dom4jXPath object here instead. Once the expression is created, I immediately bind all the namespace prefixes it uses by invoking the addNamespace() method. This location path uses two different namespace prefixes, so I call addNamespace() twice. Then I get the expression's Navigator by invoking getNavigator() . In more advanced programs, you can use the Navigator class directly to move around the tree. Here, however, I just need this to pass as an argument to another method in a few lines. Finally, I pass the context node to the selectNodes() method to get a list of all nodes in that document which satisfy the location path. In this case, the context node is the document itself because the location path is an absolute path, but in other programs, you might well pass an element or some other kind of node instead. Jaxen's selectNodes() method returns a standard java.util.List , which can be iterated through in the usual way. Because this XPath expression operated on a DOM document and returned a node-set, all of the items in the list are some form of DOM Node object. This location path only selected elements, so here they're all DOM Element objects. If the Jaxen XPath were operating on a JDOM document, then the list would contain JDOM objects. If the Jaxen XPath were operating on a dom4j document, then the list would contain dom4j objects, and so on. In most cases you'll want to cast the item in the list to some more specific type before continuing. As the program iterates through the list, it deals with each node independently. I could use the DOM methods discussed in Chapters 9 through 13 to work with these nodes, but what I really want is to get the XPath string value of each node. This is provided by the Xpath string() function, which Jaxen represents as the org.jaxen.function.StringFunction class. The evaluate() method in this class applies the XPath string function to a specified object (here a DOM Node ) in the context of a particular Jaxen Navigator . It returns the XPath string value of the object. Jaxen's org.jaxen.function package provides Java representations for most of the functions in XPath 1.0: BooleanFunction , CeilingFunction , ConcatFunction , and so on.Each of these classes has a static evaluate() method that invokes the function and returns the result. The argument lists and return types of this method change from function to function as appropriate. In a few cases in which the XPath function has a variable length argument list, the Jaxen function class uses overloaded evaluate() methods instead. These classes and their corresponding evaluate() methods are Note This is a little more convoluted than perhaps it needs to be because there's no XPath 1.0 way to return a list of strings. For example, string(node-set) returns the string value of the first node in the set rather than a list of the string values of each node in the set. That's why I have to move from XPath to DOM (where I can work with lists and sets) and back to XPath again, rather than working with a single XPath expression that returns the final result. XPath is not Turing complete. Some of the logic will need to be implemented in Java.
|