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 倍。
我有这个代码示例:
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 倍。