Extend JXPath to Generate Results in a Custom Data Model
XPath and XQuery are two of the innovations that have developed around XML. These XML-related technologies have matured, giving developers the flexibility to use them in different combinations. XPath is a simple query language for XML data storage and XQuery, an extension of XPath, is used for more complex data-selection requirements such as formatting, sorting, etc.
JXPath is an open source Java API (under the Apache Commons component list) for evaluating XPath expressions over XML and Java object models. By default, JXPath gives results in the DOM/JDOM model format, which is parser dependent. However, JXPath's design is also extensible, allowing you to customize the API to generate results for XML processing in custom model formats. This feature of JXPath is not documented and not explored by many people.
This article explores JXPath and demonstrates how to extend it to get results in a custom object model when working with an application you do not want to be coupled with the DOM/JDOM data model. The article also explains the rationale behind this customization and points out a few advantages of having your own data model.
JXPath for XML Processing
The JXPath library is built to apply XPath expressions on Plain Old Java Object (POJO) models and also on XML data. So, without processing Java objects in the Java layer, you can use XPath expressions and get the desired results.
Let's look at a simple XML file (see Figure 1), which will serve as a reference throughout the article, and walk through using JXPath with JDOM for processing XML data.
The PackageContainer class actually specifies that the above XML file be processed by the JXPath API. This class also introduces a new JXPath concept called Container. Container provides an indirection to the data. So, if a property of the context node has a Container as its value, then the XPath of that property will produce the content of the container and not the container itself. The following interface defines this responsibility:
A specific subtype called XMLDocumentContainer used to deal with XML documents, but it is now deprecated. You are supposed to use DocumentContainer instead (see Figure 2).
Note: This class's
getPackages() method actually specifies CodeElementModel.xml as the data file for the JXPath API. In this example, DOM is the model for the JXPath API.
Figure 3 shows the JUnitTest method that executes the XPath expression over XML data.
Note: In this test code, the XPath expression '
packages/Root//Method' is interpreted as follows:
packages' is a property of the PackageContainer class.
Root' is the first tag of CodeElementModel.xml.
Method' is a child of the 'Root' tag of CodeElementModel.xml at any level of the 'Root' tag.
context.getValue("packages/Root//Method"); will pick the value of
method@name='payByVisaCreditCard' "Does the Payment for the Visa Credit Cards", and
context.iterate("packages/Root//Method") will pick up all the methods.
To this point, you have seen how to use the standard JXPath API to process simple XPath expression over XML input. You have also seen what the role of the Container interface is. In this section, you got the results in the DOM model. In the next section, you will see how to extend the library to get results in a custom model.
The Case for Custom Models
Suppose you need to have a generic domain model for heterogeneous data structures, and you sometimes use service data objects (SDO) or your own custom model. So, instead of writing a model translator that does JDOM/DOM model-to-custom model translations, you can modify JXPath API for a more efficient solution.
Modifying JXPath is more efficient in applications where XPath is the dominant XML-processing solution and each time you process XML you need to do translation of a model as well. Customizing JXPath will take some effort, but it's a one-time effort.