http://www.developer.com/java/data/article.php/3867136/Extend-JXPath-to-Generate-Results-in-a-Custom-Data-Model.htm
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. 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 Figure 3 shows the JUnitTest method that executes the XPath expression over XML data. Note: In this test code, the XPath expression ' The 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. 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. Figure 4 shows the custom data model that is used in the code download, which would be generated by JXPath and the SAX parser. In this model: Note: There are interfaces for Attribute, Property and DynamicProperty, which are not shown in the class diagram to keep it simple. 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. Figure 5 illustrates how you can use pointers. The next section explains the relation between the basic JXPath components. Here are the components and their respective roles: 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. 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: Figure 8 shows the 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: Figure 9 shows the 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: 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. Figure 11 shows the Pointer Factory class diagram. 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. 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 In Figure 13, the custom node pointer factory is added in the JXPathContextReferenceImpl type before calling 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. The code snippets in Figures 14 and 15 show how to create an XML container with a new parser using a new Key. Figure 14 shows the In Figure 14, the XML Parser (com.jxpath.setl.jw.sample.genericxmlparser.XMLSAXParser) object is registered to the JXPath framework by calling In Figure 15, DocumentContainer is created with the model as Note: You need to use same Key This concludes the JXPath API explanation. The final section briefly discusses the data model used throughout the article. You now know the details involved in extending the JXPath API to derive a custom model while processing XPath language expressions over XML data. This way, you can extend the XPath engine, or even other XQuery engines that support extensions, to produce custom data models in different types of enterprise applications, as XML is used mostly as a means of data transfer. These applications could range from data integration to dynamic configuration. Also, there many areas where you can improve from the example. You, for example, could have a more efficient algorithm for evaluating XPath expression. You could have a single layer of query for RDBMS and XML, an abstraction for querying that will hide the type of repository from which the data is fetched. So, the same query could be used seamlessly to interact with XML or a RDBMS.
Extend JXPath to Generate Results in a Custom Data Model
February 25, 2010
JXPath for XML Processing
Figure 1. Sample XML Data for This Example org.apache.commons.jxpath.Container
Figure 2. Package Container for This Example 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. Package The Test JXPath XML Query for This Example 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.The Case for Custom Models
How to Extend/Modify JXPath
Figure 4. The Custom Data Model for This Example
JXPath Extension Points
JXPathContext.newContext() is the static method provided on Context to get the object.Component Diagram
The view in Figure 7 will help illustrate the relationship between the main components of the JXPath API.
Figure 7. The Main Components of the JXPath API
Pointer
You need to have custom implementations of the following abstract classes related to Pointer abstraction:
Figure 8. Pointer Class Diagram
Figure 9. Iterator Class Diagram NodePointer createNodePointer(NodePointer parent, QName name, Object object);
Figure 10. Create NodePointer Object
Figure 11. Pointer Factory Class Diagram XML Parser
Figure 12. XML Parser Class Diagram JXPathContext
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.
Figure 13. Test JXPath Model XML Query 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.Using JXPath After Extension for a Custom Model
getPackage() method from the model class MyModelPackageContainer and Fig 15 shows the setXMLParser method, two methods for registering a custom parser and model.
Figure 14. MyModelPackageContainer Model Class getPackage
Figure 15. MyModelPackageContainer Model Class setXMLParser DocumentContainer.registerParser("SAMPLE_PARSER", xmlParser)."SAMPLE_PARSER".(SAMPLE_PARSER) while registering a parser and model and while creating DocumentContainer.What Have You Learned?
Code Download