How to use Saxon XPath 2.0 with Java?

10,409

Solution 1

(while this is the accepted answer currently, there is an important piece of information in Michael Kay's answer below, which I think is a better answer to this question, but I can't change the accepted-answer flag...)

It is quite easy to setup a transformation in Java using Saxon, just refer to the documentation here. Another straightforward how-to can be found here. Other examples are scattered all over the Net.

The XPath API for Java is documented here at Saxonica.

Note, your link is referencing a version 7.7. You downloaded version 9.6. If you want to use samples from 7.7, you should probably download that version. They may still be somewhat accurate, but I'm not sure.

Solution 2

In the latest release of Saxon, the JAR file no longer contains the meta-inf services file advertising it as a JAXP XPath factory provider. This is because too many applications were having problems: if the application is written and tested to work with the XPath 1.0 engine that comes with the JDK, there is a good chance it will fail if you try running it with Saxon's XPath 2.0 engine, and this was happening simply as a result of Saxon being on the class path. So if you want to use Saxon as your XPath engine, you now have to make the request explicit, e.g. by instantiating net.sf.saxon.xpath.XPathFactoryImpl directly.

By that I mean calling new XPathFactoryImpl() instead of XPathFactoryImpl.newInstance() because it's inherited from XPathFactory.

However, because the XPath 2.0 type system is so much richer than XPath 1.0, the JAXP interface is really quite clumsy, and I would recommend using the s9api interface instead.

Solution 3

I use Saxon-HE 9.8.0-5 like this:

Processor processor = new Processor(false);
XdmNode xdm = processor.newDocumentBuilder().build(new StreamSource(new StringReader(xml)));
XdmValue result = processor.newXPathCompiler().evaluate(query, xdm);

StringBuilder sb = new StringBuilder();
for (int i = 0; i < result.size(); i++) {
    sb.append(result.itemAt(i).getStringValue());
    if (i + 1 != result.size()) {
        sb.append('\n');
    }
}

Solution 4

Using the S9API is never as simple as it should be. Here's the simplest working example that I could create. It's a complete unit test that works with Saxon 9.7 HE in NetBeans 8.

Assuming this XML (simple.xml):

<person>
    <firstname>James</firstname>
    <age>25</age>
</person>

Run this unit test, which is looking for the firstname node:

import java.io.File;
import java.util.Iterator;
import java.util.List;
import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathFactoryConfigurationException;
import net.sf.saxon.Configuration;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.DocumentInfo;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.xpath.XPathFactoryImpl;
import org.junit.Test;
import org.xml.sax.InputSource;
import static org.junit.Assert.*;

public class xPath2
{
    @Test
    public void mytest() throws XPathFactoryConfigurationException, XPathException, XPathExpressionException     
    {
        System.setProperty("javax.xml.xpath.XPathFactory:" + NamespaceConstant.OBJECT_MODEL_SAXON, "net.sf.saxon.xpath.XPathFactoryImpl");
        XPathFactory xPathFactory = XPathFactory.newInstance(NamespaceConstant.OBJECT_MODEL_SAXON);
        XPath xPath = xPathFactory.newXPath();
        InputSource inputSource = new InputSource(new File("simple.xml").toURI().toString());
        SAXSource saxSource = new SAXSource(inputSource);
        Configuration config = ((XPathFactoryImpl) xPathFactory).getConfiguration();
        DocumentInfo document = config.buildDocument(saxSource);      
        String xPathStatement = "//firstname";
        XPathExpression xPathExpression = xPath.compile(xPathStatement);  
        List matches = (List) xPathExpression.evaluate(document, XPathConstants.NODESET); 
        if (matches != null)
        {
            for (Iterator iter = matches.iterator(); iter.hasNext();)
            {
                NodeInfo node = (NodeInfo) iter.next();
                assertEquals("firstname", node.getDisplayName());
                assertEquals("James", node.getStringValue()); 
            }
        }
    }
}

This is a simplified version of one of the examples that come with the Saxon 9.7 resources (http://www.saxonica.com/download/download_page.xml).

Note that the xPath statement isn't xPath 2.0, but this code will work with a 2.0 statement.

Share:
10,409
StellaMaris
Author by

StellaMaris

The world is so spacious... and the head is so limited!

Updated on June 05, 2022

Comments

  • StellaMaris
    StellaMaris about 2 years

    I like to use regular expressions in xPath, so I installed Saxon9.6

    1. My ${java.home} is C:\Program Files\Java\jdk1.7.0_51.
    2. I extracted saxonHE9-6-0-6J.zip in C:\Program Files\Java\jdk1.7.0_51\jre\lib\ext
    3. and add saxonhe9.jar to my classpath variable.
    4. Then I created a jaxp.properties file under C:\Program Files\Java\jdk1.7.0_51\jre\lib and add the following lines:

      javax.xml.transform.TransformerFactory = net.sf.saxon.TransformerFactoryImpl javax.xml.xpath.XPathFactory","net.sf.saxon.xpath.XPathFactoryImpl

    But now I cant find the examples like it is described on this page.