gooamoko
gooamoko

Reputation: 658

Saxon and XPath. Can't understand

I have a method for testing

    private String getXmlVersion(byte[] xml) throws Exception {
        String expression = "//Document/@version";
        XPathFactoryImpl xPathFactory = new XPathFactoryImpl();
        XPath xPath = xPathFactory.newXPath();
//        DocumentBuilder db = new DocumentBuilderFactoryImpl().newDocumentBuilder();
//        org.w3c.dom.Document xmlDoc = db.parse(new ByteArrayInputStream(xml));
        XPathExpression ex = xPath.compile(expression);
//        return  "" + ex.evaluate(xmlDoc, XPathConstants.STRING);
        return  "" + ex.evaluate(new InputSource(new ByteArrayInputStream(xml)), XPathConstants.STRING);
    }

I am using net.sf.saxon.dom.DocumentBuilderFactoryImpl and net.sf.saxon.xpath.XPathFactoryImpl. This version doesn't pass the test, cos XPath evaluates as empty string. But if I uncomment lines and start to use DocumentBuilder - XPath evaluates as expected and all tests will be passed.

In documentation I read that...

It is important to note that a compiled XPath expression can only be used with a source document that was built using the same Saxon Configuration.

Seems like when I use both DocumentBuilderFactoryImpl and XPathFactoryImpl, their Configuration is differs, but tests is passed. But what's wrong with a way when I use only XpathFactoryImpl?

I am using saxon 9.2.0.5.

can anyone help me by example of correct way using Saxon and XPath? Thank you for wasting your time for me and best regards.

EDIT I have tried to use s9api for that and here is a method example

private String getVersion(byte[] xmlData) throws Exception {
    Processor proc = new Processor(false);
    DocumentBuilder builder = proc.newDocumentBuilder();
    XPathCompiler xpc = proc.newXPathCompiler();
    xpc.declareNamespace("", "http://namespaceurl.info/ver2/rev2");

    XPathSelector selector = xpc.compile("//Document/@version").load();
    selector.setContextItem(builder.build(new StreamSource(new ByteArrayInputStream(xmlData))));
    XdmItem xdmItem = selector.evaluateSingle();
    return xdmItem == null ? "" : xdmItem.getStringValue();
}

This method pass a half of the tests, because it used explicitly defined namespace. But in different versions of documents namespaces is different.

Upvotes: 3

Views: 648

Answers (1)

Michael Kay
Michael Kay

Reputation: 163595

The best way to use Saxon and XPath is to use Saxon's s9api API rather than the JAXP API. There are several advantages to this:

(a) the JAXP API is designed around XPath 1.0 rather than 2.0/3.1

(b) the JAXP API, although it tries to be object-model-neutral, is designed around DOM, while for Saxon, DOM is probably the least efficient tree model of all those supported.

(c) s9api is a lot more usable (type safety, thread safety, binding of namespaces, binding of external variables and functions).

If you do want to use the JAXP XPath API with Saxon, and to run it against a DOM tree, then it can be done.

You haven't said which version of Saxon you are using. The DocumentBuilderFactoryImpl was deprecated in 9.3, and dropped some time before 9.6 (the current release is 9.8).

At this stage it depends a little what you want to do. Do you want to run several XPath expressions against the source tree, or just one? If it's just one, you can supply a StreamSource and let Saxon build the tree in whatever way it considers most efficient. If it's several, then you should build the tree yourself. Does it have to be a DOM tree, given that the DOM is far less efficient than other models (notably than Saxon's own TinyTree). If it does, then you can simply supply a DOMSource to the XPath evaluation. If it doesn't, then build a TinyTree - at which point you are definitely better off using s9api interfaces rather than JAXP.

Upvotes: 1

Related Questions