January 19, 2021
Hot Topics:

Extend JXPath to Generate Results in a Custom Data Model

  • By Manish Malhotra
  • Send Email »
  • More Articles »

How to Extend/Modify JXPath

Figure 4 shows the custom data model that is used in the code download, which would be generated by JXPath and the SAX parser.

The Custom Data Model for This Example
Figure 4. The Custom Data Model for This Example

In this model:

  1. Attribute represents the attribute of the XML node.
  2. Property represents the node of an XML data graph. This represents a leaf node and not a parent node.
  3. DataEntity represents a parent node of an XML data graph.
  4. DynamicProperty represents dynamic properties of nodes, such as line no., column no., or any user-defined data.

Note: There are interfaces for Attribute, Property and DynamicProperty, which are not shown in the class diagram to keep it simple.

JXPath Extension Points

This section is about the extension points of JXPath. JXPath is designed such that these extension points are used to generate results in a custom object model for XML data input, instead of in a DOM or JDOM data model.

JXPath supports multiple models through the notion of different abstractions. Basic JXPath abstractions that you need to understand are Pointer, XMLParser, and JXPathContext.

  • Pointer: The notion of a pointer is the same as what you have in programming languages like C, which uses the pointer as an address that points to a variable rather than actually holding data. Similarly, in JXPath, if you need to find where the node in the graph is and do not want to get the actual node from the object graph itself, then you can use pointers.

    Figure 5 illustrates how you can use pointers.

    The Test JXPath Pointers for This Example
    Figure 5. The Test JXPath Pointers for This Example

  • XMLParser: The XML parser abstraction is provided by the abstract class org.apache.commons.jxpath.xml.XMLParser2 (see Figure 6). JXPath provides implementation of this abstraction for different types of models.

    The XML Parser for This Example
    Figure 6. The XML Parser for This Example

  • JXPathContext: This is the important abstraction of the JXPath API; it provides access to the traversal of JavaBean and XML graphs using XPath syntax. It also provides a factory to create JXPathContext objects, JXPathContextFactory. This factory enables control over the creation of JXPathContext, and it can have multiple implementations of JXPathContext and return that particular object from factory. JXPathContext.newContext() is the static method provided on Context to get the object.

The next section explains the relation between the basic JXPath components.

Component Diagram

The view in Figure 7 will help illustrate the relationship between the main components of the JXPath API.

The Main Components of the JXPath API
Figure 7. The Main Components of the JXPath API

Here are the components and their respective roles:

  1. JXPath Engine is the core of the JXPath API. It uses NodePointers and Parsers to evaluate the XPath expression over the XML document/Java model. JXPath Engine is being modified so that it will use a new Parser/NodePointer implementation apart from the older one, which is required to get results in the form of DOM/JDOM Data model.
  2. The JXPath-XML Parser component allows you to create a new implementation of XML Parser. This example uses an implementation of a SAX-based XML parser, which parses the XML and populates the result in the generic Data Model objects. This component will be used by Engine to parse the XML and get the parsed results.
  3. The JXPath-NodePointer component is called by JXPath Engine after it parses the XML document using the JXPath-XML Parser component. This component includes NodePointer, NodeIterator and NodePointerFactory abstractions. The example implements NodePointer, NodeIterator and NodePointerFactory abstractions of the JXPath API, these implementations are required for a new data model.

You now have a fair understanding of the basic abstractions of the JXPath API, its component-level relationship, and so on. Now it's time to see the details of all the classes/interfaces that need to be extended and how they are supposed to be used.


You need to have custom implementations of the following abstract classes related to Pointer abstraction:
  1. NodePointer – JXPath Type: org.apache.commons.jxpath.ri.model.NodePointer
  2. NodeIterator – JXPath Type: org.apache.commons.jxpath.ri.model.NodeIterator
  3. NodePointerFactory – JXPath Type: org.apache.commons.jxpath.ri.model.NodePointerFactory

Node Pointer represents the location of a node in an object graph. In XML, it could represent a node, attribute or namespace. Here are the new Pointer implementation classes (XML tag, namespace and attribute) for this example:

  • org.apache.commons.jxpath.ri.model.ia.MyModelNodePointer
  • org.apache.commons.jxpath.ri.model.ia.MyModelNamespacePoniter
  • org.apache.commons.jxpath.ri.model.ia.MyModelAttributePoniter

Figure 8 shows the Pointer class diagram.

Pointer Class Diagram
Figure 8. Pointer Class Diagram

Node Iterator is an iterator for any kind of node (attribute, namespace or tag). Here are the new Iterator implementations (XML tag, namespace and attribute) for this example:

  • org.apache.commons.jxpath.ri.model.ia.MyModelNodeIterator
  • org.apache.commons.jxpath.ri.model.ia.MyModelNamespaceIterator
  • org.apache.commons.jxpath.ri.model.ia.MyModelAttributeIterator

Figure 9 shows the Iterator class diagram.

Iterator Class Diagram
Figure 9. Iterator Class Diagram

The Node Pointer Factory is based on the Factory design pattern. The Factory pattern is a creational pattern in which the Factory method hides the complexity of creating the resultant object. NodePointerFactory specifies factory methods to create the NodePointer object by allowing you to pass Object as a formal parameter. This object is one of the model objects:

NodePointer createNodePointer(NodePointer parent, QName name,  Object object);

Figure 10 shows an implementation of the above method. IDataElement and Property are the new data model classes, and the new Factory class is org.apache.commons.jxpath.ri.model.ia.MyModelNodePointerFactory.

Create NodePointer Object
Figure 10. Create NodePointer Object

Figure 11 shows the Pointer Factory class diagram.

Pointer Factory Class Diagram
Figure 11. Pointer Factory Class Diagram

XML Parser

This example needs to have a custom implementation of an abstract XMLParser, which represented by org.apache.commons.jxpath.xml.XMLParser2. This component parses XML and gives a relevant model to JXPath Engine for further evaluation. The example implementation, com.jxpath.setl.jw.sample.genericxmlparser.XMLSAXParser, uses a SAX-based parser and an implemented XMLParser2 interface. This parser parses any XML and populates the memory model using a generic model graph, on which JXPath applies XPath expression and returns the result in the generic model result.

Figure 12 shows the XML parser class diagram.

XML Parser Class Diagram
Figure 12. XML Parser Class Diagram


JXPathContext adds a node pointer factory by adding it to the JXPathContextReferenceImpl. This allows you to pass any custom NodePointerFactory to the JXPath API. However, as the method provided by JXPath is in the implementation of JXPath, client code will be coupled with the implementation of JXPath, which might not be preferable in some cases. The other option is to change some code in JXPathContextReferenceImpl to register NodePointerFactory, so that by default that factory will be added.

You can achieve this by calling a static method called public static void addNodePointerFactory(NodePointerFactory factory) of the org.apache.commons.jxpath.ri.JXPathContextReferenceImpl class. Figure 13 shows a code snippet illustrating how to use this option to configure the custom node pointer factory.

Test JXPath Model XML Query
Figure 13. Test JXPath Model XML Query

In Figure 13, the custom node pointer factory is added in the JXPathContextReferenceImpl type before calling JXPathContext.newContext(cont); because before getting a new object of JXPathContextReferenceImpl, which initializes the other node pointer factories, you need to add your custom one. Sequence is important.

Also notice that the class called MyModelPackageContainer is passed to create a new JXPathContext. This class is also a little different from the older PackageContainer (see Figure 2) as it has to register a new XML parser and a new model type.

Page 2 of 3

This article was originally published on February 25, 2010

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date