Project

Profile

Help

Java - How to manage different expressions in the same way

Added by Gabriel Dal Borgo over 4 years ago

Hi! I have this code sample:

package com.test.xml;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl;
import net.sf.saxon.xpath.XPathFactoryImpl;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilderFactory;

import static javax.xml.xpath.XPathConstants.NODESET;
import static org.w3c.dom.Node.ATTRIBUTE_NODE;

public class MyMainClass {

    public static void main(String[] args) throws XPathExpressionException, IOException, SAXException, ParserConfigurationException {
        Arrays.stream(args).forEach(System.out::println);

        DocumentBuilderFactory factory = new DocumentBuilderFactoryImpl();
        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
        Document document = documentBuilder.parse(new File(MyMainClass.class.getClassLoader().getResource("demo.xml").getFile()));
        Node root = document.getFirstChild();

        String expression = "//TestAttribute/@value";
        XPathFactory xpathFactory = new XPathFactoryImpl();
        XPath xpath = xpathFactory.newXPath();
        XPathExpression xpathExpression = xpath.compile(expression);
        Object evaluation = xpathExpression.evaluate(root, NODESET);
        List<String> list = toStringList((NodeList)evaluation);

        list.forEach(System.out::println);
    }

    private static List<String> toStringList(NodeList nodeList) {
        final int size = nodeList.getLength();
        List<String> strings = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            Node item = nodeList.item(i);
            if (item.getNodeType() == ATTRIBUTE_NODE) {
                strings.add(item.getTextContent());
            }
        }
        return strings;
    }
}

The content of 'Demo.xml' is:

<?xml version="1.0"?>
<test:Demo xmlns:test="http://java.sun.com/xml/ns/jaxb">
    <TestAttribute value="something" />
    <TestAttribute value="other" />
</test:Demo>

Executing that I get the result:

something
other

That's is working OK and I dont have any problem. Now I want to support another kind of expression like: namespace-uri(/*)

If I replace this expression:

        String expression = "namespace-uri(/*)";
        XPathFactory xpathFactory = new XPathFactoryImpl();
        XPath xpath = xpathFactory.newXPath();
        XPathExpression xpathExpression = xpath.compile(expression);
        Object evaluation = xpathExpression.evaluate(root, NODESET);
        List<String> list = toStringList((NodeList)evaluation);

I get this error:

Exception in thread "main" net.sf.saxon.trans.XPathException: Cannot convert XPath value to Java object: required class is org.w3c.dom.NodeList; supplied value has type xs:anyURI
	at net.sf.saxon.dom.DOMObjectModel.convertXPathValueToObject(DOMObjectModel.java:422)
	at net.sf.saxon.dom.DOMObjectModel$7.convert(DOMObjectModel.java:205)
	at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:242)
	at com.test.xml.MyMainClass.main(MyMainClass.java:41)
--------------- linked to ------------------
javax.xml.xpath.XPathExpressionException: net.sf.saxon.trans.XPathException: Cannot convert XPath value to Java object: required class is org.w3c.dom.NodeList; supplied value has type xs:anyURI
	at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:247)
	at com.test.xml.MyMainClass.main(MyMainClass.java:41)
Caused by: net.sf.saxon.trans.XPathException: Cannot convert XPath value to Java object: required class is org.w3c.dom.NodeList; supplied value has type xs:anyURI
	at net.sf.saxon.dom.DOMObjectModel.convertXPathValueToObject(DOMObjectModel.java:422)
	at net.sf.saxon.dom.DOMObjectModel$7.convert(DOMObjectModel.java:205)
	at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:242)
	... 1 more

My questions are:

  • how can I manage this differents expressions?
  • how can I evaluate the expressions and then check the type?

Thank you!


Replies (2)

RE: Java - How to manage different expressions in the same way - Added by Michael Kay over 4 years ago

This is one of the weaknesses of the JAXP XPath API, which becomes particularly acute when you move to XPath 2.0 with its richer type system: when you execute an XPath expression, you have to say what kind of result is expected.

I would recommend moving to the s9api API. In s9api, you don't have to say what type of result you are expecting. XPathSelector.evaluate() for this expression will return an XdmAtomicValue. You can determine its XDM type using the method getTypeName().

Note that unless you have a very strong reason for using DOM as your tree model, I would not recommend it. It's a poorly designed API, isn't well aligned with XDM (especially in the area of namespaces) and isn't thread-safe. Use Saxon's default TinyTree in preference: it can be a factor of 10 faster.

RE: Java - How to manage different expressions in the same way - Added by Michael Kay over 4 years ago

I'd be grateful if you don't cross post on this forum and on StackOverflow. It gets very confusing when people find the question in one place or another and it appears to be unanswered. You're welcome to use either forum, but please don't use both.

    (1-2/2)

    Please register to reply