Java - XPath - 如何以相同的方式管理不同的表达式

Java - XPath - How to manage different expressions in the same way

我有这个代码示例:

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;
    }
}

'Demo.xml'的内容是:

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

执行得到结果:

something
other

一切正常,我没有任何问题。现在我想支持另一种表达式,例如:namespace-uri(/*)

如果我替换这个表达式:

        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);

我收到这个错误:

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.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.convert(DOMObjectModel.java:205)
    at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:242)
    ... 1 more

我的问题是:

谢谢!

您在 saxon-help 论坛上问过同样的问题。作为记录,这是我的答案。以后请避免交叉发布。

这是 JAXP XPath 的弱点之一 API,当您迁移到具有更丰富的类型系统的 XPath 2.0 时,这个弱点变得尤为严重:当您执行 XPath 表达式时,您必须说明哪种类型预期结果。

我建议转向 s9api API。在 s9api 中,您不必说出您期望的结果类型。此表达式的 XPathSelector.evaluate() 将 return XdmAtomicValue。您可以使用方法 getTypeName() 确定其 XDM 类型。

请注意,除非您有非常充分的理由使用 DOM 作为您的树模型,否则我不会推荐它。它的设计很糟糕 API,与 XDM 不一致(尤其是在名称空间领域)并且不是线程安全的。优先使用 Saxon 的默认 TinyTree:它可以快 10 倍。