Wednesday, January 4, 2012

How to execute the XSLT functions as a part of XPath expressions in Java using Xalan parser

Executing XPath is a very common requirement in Java applications that deal with XML data. When there is a decision making requirement based on multiple data records, spanned across the xml document, it would be more efficient to use XSLT functions to extract information, than to parse and navigate the xml document back and forth.

XSLT includes several functions that help users extract information from XML data. It consists of many string, date and time, sequence manipulation, qname manipulation, aggregation and mathematical functions. Few of the most popular functions are given below




String related:
string(arg)
concat(string,string,...)
string-join((string,string,...),sep)
substring(string,start,len)
contains(string1,string2)
replace(string,pattern,replace)
matches(string,pattern)
tokenize(string,pattern)

Date and time related:
dateTime(date,time)
adjust-dateTime-to-timezone(datetime,timezone)
year-from-dateTime(datetime)

Sequence related:
index-of((item,item,...),searchitem)
remove((item,item,...),position)
exists(item,item,...)
reverse((item,item,...))

Maths, number, set related
abs(num)
round(num)
ceiling(num)
floor(num)
max((arg,arg,...))
min((arg,arg,...))
avg((arg,arg,...))
count((item,item,...))
sum(arg,arg,...)

Let us take a simple example. Assume we have an xml that is the result of a book search from the bookstore application. Below is the xml data.

<?xml version="1.0" encoding="ISO-8859-1"?>
<results>
<book category="COOKING">
  <title lang="en">Everyday Italian</title>
  <author>Giada De Laurentiis</author>
  <year>2005</year>
  <price>30.00</price>
  <date>2011-10-20</date>
</book>

<book category="CHILDREN">
  <title lang="en">Harry Potter</title>
  <author>J K. Rowling</author>
  <year>2005</year>
  <price>29.99</price>
  <date>2008-01-10</date>
</book>

<book category="WEB">
  <title lang="en">XQuery Kick Start</title>
  <author>James McGovern</author>
  <author>Per Bothner</author>
  <author>Kurt Cagle</author>
  <author>James Linn</author>
  <author>Vaidyanathan Nagarajan</author>
  <year>2003</year>
  <price>49.99</price>
  <date>2008-01-10</date>
</book>

<book category="WEB">
  <title lang="en">Learning XML</title>
  <author>Erik T. Ray</author>
  <year>2003</year>
  <price>39.95</price>
  <date>2007-01-10</date>
</book>

</results>

An efficient way to find the book that has the lowest price would be to execute the XSLT function min(/results/book/price).

These kind of operations are called extension functions, in the sense that, they extend the core library of functions that XPath provides. Xalan-Java supports extension elements and extension functions and also provides a growing extensions library for most of our needs. You can access the functions by providing the proper prefix. For eg: Since min is defined in the math functions, it will have a prefix of math

Complete set of extension functions available in Xalan-Java and their prefix is given below

EXSLT common functions – exslt
EXSLT math functions – math
EXSLT set functions – set 
EXSLT date-and-time functions – datetime
EXSLT dynamic functions – dyn
EXSLT string functions – str

Let us see how to execute this XPath expression in Java using xalan apis

Include the below packages

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.xalan.extensions.ExtensionNamespaceContext;
import org.apache.xalan.extensions.XPathFunctionResolverImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

Then create the Xpath

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();


In order to execute extension functions along with the XPaths, we need to add the ExtensionsNameSpace context to the xpath

xpath.setNamespaceContext(new ExtensionNamespaceContext());
xpath.setXPathFunctionResolver(new XPathFunctionResolverImpl());

And then compile the xpath

XPathExpression compiledExpr = xpath.compile(xpathExpr);

Now we are ready to execute this xpath against an XML document

For the sake of testing, let us assume the results are in a text file named bookstore.xml(This can be done with xml data in String as well). The below code will run the xpath expression and return the result

public class TestXPathExtension {
        public static Object executeXpath(Node node, String xpathExpr,
                        QName returnType) throws Exception {

                Object returnValue = null;
                XPathFactory xpathFactory = XPathFactory.newInstance();
                XPath xpath = xpathFactory.newXPath();
                xpath.setNamespaceContext(new ExtensionNamespaceContext());
                xpath.setXPathFunctionResolver(new XPathFunctionResolverImpl());
                XPathExpression compiledExpr = xpath.compile(xpathExpr);
                returnValue = compiledExpr.evaluate(node, returnType);

                return returnValue;
        }




        public static void main(String[] args) {
                String xmlName = "bookstore.xml";
                String xpathExpr = "math:min(/results/book/price)";

                DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder domBuilder;
                Document doc = null;

                try {

                        FileInputStream fis = new FileInputStream(xmlName);
                        domBuilder = domFactory.newDocumentBuilder();
                        doc = domBuilder.parse(fis);

                    System.out.println(executeXpath(doc,xpathExpr,XPathConstants.NUMBER));

                    fis.close();
                } catch (Exception e) {
                        e.printStackTrace();
                }

        }

}

Output: 29.99


Blog Archive