October 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

XPath Rules!

  • November 19, 2003
  • By Jeff Ryan
  • Send Email »
  • More Articles »

XPath Rule Processor

From our discussion of the schemas and sample documents, you should have a good idea of what the inputs and output of the rule processor will be. Let's step through the code and see how it performs its task of asserting the rules in the rules document versus the model document and outputting the errors found.

Where possible, we are using standard Java API for XML (JAXP 2.0). However, DOM XPath capability is lacking in these interfaces. We'll use the XPath capabilities in Apache Xalan to fill in the gaps.

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.apache.xpath.XPathAPI;
import org.apache.xpath.objects.XBoolean;

We begin by opening up our rules document, "Rules.xml" and our model document, "Customer.xml" using the standard JAXP APIs. We get the document element for each document for subsequent processing.

public static void main(String[] args) throws Exception
{
  DocumentBuilderFactory factory =
    DocumentBuilderFactory.newInstance();
  DocumentBuilder builder = factory.newDocumentBuilder();
  Document rules = builder.parse("Rules.xml");
  Node rulesRoot = rules.getDocumentElement();
  Document model = builder.parse("Customers.xml");
  Node modelRoot = model.getDocumentElement();

Next, we get a NodeList of all the rules we are going to assert versus the model document. In our sample rules document, this will return three nodes. Notice how using XPath with DOM makes for much more understandable and less verbose code than DOM programming by itself. A StringBuffer is also prepared to hold the errors document.

NodeList ruleNodes = XPathAPI.selectNodeList(rulesRoot,
                                             "/Rules/Rule");

StringBuffer errors = new StringBuffer("<Errors>");

Now the real fun begins. The outer loop iterates through the rules to be processed.It gets the context, assertion, and message elements for a given rule. Then, it uses the context to get a NodeList from the model document of all the elements the rule needs to be run against. When run versus our example model document, this will return the five Customer nodes.

for (int i = 0; i < ruleNodes.getLength(); i++)
{
  Node rule = ruleNodes.item(i);
  String context =
    XPathAPI.selectSingleNode(rule, "Context/text()")
      .getNodeValue();
  String assertion = 
    XPathAPI.selectSingleNode(rule, "ValidationAssertion/text()")
      .getNodeValue();
  String message =
    XPathAPI.selectSingleNode(rule, "ErrorMessage/text()")
      .getNodeValue();

  NodeList modelNodes = XPathAPI.selectNodeList(modelRoot, context);

The inner loop iterates through the model document nodes. Using the eval() method in XPathAPI, the rule assertion is made versus a given model node. If the assertion evaluates to false, we add an Error element to the Errors document that includes the error message and context.

for (int j = 0; j < modelNodes.getLength(); j++)
{
  Node modelNode = modelNodes.item(j);
  XBoolean result = (XBoolean)XPathAPI.eval(modelNode, assertion);
  if (!result.bool())
  {
    String path = getAbsolutePath((Element)modelNode);
    errors.append("<Error>"
          + "<Context>" + path + "</Context>"
          + "<Message>" + message + "</Message>"
          + "</Error>");
    }
  }
}
errors.append("</Errors>");
System.out.println(errors.toString());

The getAbsolutePath() method returns an XPath expression representing the ContextNode which violated the rule. It recursively ascends the DOM tree to build the expression. Any nodes uniquely identified with id attributes are also added to the XPath expression.

public static String getAbsolutePath(Element e)
{
  String path = "/" + e.getTagName();
  if (e.hasAttribute("id"))
  {
    path += "[@id='" + e.getAttribute("id") + "']";
  }
  Node parent = e.getParentNode();
  if (parent.getNodeType() == Node.ELEMENT_NODE)
  {
    return getAbsolutePath((Element)parent) + path;
  }
  else
 {
    return path;
  }
}

When we run the XPathRuleProcessor using the Rules.xml and Customers.xml document we presented earlier, the following results are produced:

<Errors>
  <Error>
    <Context>/Customers/Customer[@id='C4']</Context>
    <Message>Customer name must be less than 30
     characters</Message>
  </Error>
  <Error>
    <Context>/Customers/Customer[@id='C5']</Context>
    <Message>Customer name must be greater than 3
     characters</Message>
  </Error>
  <Error>
    <Context>/Customers/Customer[@id='C5']</Context>
    <Message>City must be greater than 3 characters</Message>
  </Error>
  </Errors>

With a few lines of code, we wrote a truly generic rule engine. Rule documents could be authored without programming to validate any domain model schema. Our rule processor would need a bit of work to be production quality. However, it illustrates how a simple rule assertion algorithm can be implemented using XPath as the rule language.

Pros and Cons of This Approach

The high-level conceptual model presented here could be used to implement a lightweight rules framework. Most Java shops already have the tools in house to build such a framework. Because a declarative approach is used, new rules can be added without programming, reducing maintenance costs.

You wouldn't expect a declarative XPath rules engine to perform up to the capabilities of an industrial-strength rule engine with rules represented in compiled code. However, performance could be quite acceptable.

You are probably wondering why you would use an XPath rule engine as described in this article when XML Schemas could be used to validate a domain model document. There are several reasons why you might consider this. There may be semantic or syntactic checks that cannot be implemented in XML Schema. In addition, the error violations reported by a validating parser may not be granular enough or friendly enough to meet user requirements.

Summary

XPath is a language for addressing elements in XML. But, it also can be used to assert expressions versus XML documents. We used the latter capability along with some basic DOM programming to build a simple rule engine. The conceptual model and code presented here could be the genesis of a validation framework or other lightweight rules framework for your next project. But, the rest is up to you!

Code Examples

To download the XML documents, schemas, and Java code, click here.

About the Author

Jeff Ryan is an architect for Hartford Financial Services. He has twenty years experience designing, developing and delivering automated solutions to business problems. His current focus is on Java, XML and Service Oriented Architecture. He may be reached at jryan@thehartford.com.
Other Articles Written by Jeff Ryan

End Notes

[i] Apache Xalan Home Page

[ii] ACORD XML Specification





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel