萨克森XPath API返回TinyElementImpl而不是org.w3c.dom.Node

8

我有以下代码:

// xpath evaluates to net.sf.saxon.xpath.XPathEvaluator
XPath xpath = XPathFactory.newInstance().newXPath(); 
XPathExpression expression = xpath.compile("/foo/bar");
Object evaluate = expression.evaluate(someXML, XPathConstants.NODE);
Object evaluate2 = expression.evaluate(someXML, XPathConstants.NODESET);

System.out.println(evaluate!=null?evaluate.getClass():"null");
System.out.println(evaluate2!=null?evaluate2.getClass():"null2");

System.out.println(evaluate instanceof Node);
System.out.println(evaluate2 instanceof NodeList);

这是结果...

net.sf.saxon.tinytree.TinyElementImpl类
java.util.ArrayList类
false
false

仅做澄清,如果我这样做:

org.w3c.dom.Node node = (org.w3c.dom.Node)evaluate;

或者
org.w3c.dom.NodeList node = (org.w3c.dom.NodeList)evaluate2;

我遇到了一个ClassCastException

这怎么可能呢?根据Sun的Java 1.5API,NODE和NODESET应该分别映射到org.w3c.dom.Nodeorg.w3c.dom.NodeList

仅作澄清2是的,我知道Node是一个接口,getClass()返回一个具体的类。

5个回答

7

好的,我想通了!

如果evaluate方法收到一个InputSource,则会出现上述错误。

例如:

InputSource someXML = new InputSource(new StringReader("<someXML>...</someXML>)");
Object result = expression.evaluate(someXML, XPathConstants.NODE); 
Node node = (Node) result; // ClassCastException

结果没有实现 org.w3c.dom.NodeTinyElementImpl)。

但如果 evaluate 接收到一个 Node(或者一个 Document):

DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = builderFactory.newDocumentBuilder();
Document someXML = documentBuilder.parse(new InputSource(new StringReader("<someXML>...</someXML>)"));
Object result = expression.evaluate(someXML, XPathConstants.NODE);
Node node = (Node) result; // works

虽然可以正常工作,但这仍然很奇怪...


有道理...如果你输入 W3C DOM,你将得到 W3C DOM。否则,你会得到专有的 DOM。 - skaffman

3

尝试这段代码:

Object evaluate = expression.evaluate(someXML, XPathConstants.NODE);
System.out.println(evaluate instanceof Node);
System.out.println(NodeOverNodeInfo.wrap((NodeInfo) evaluate) instanceof Node);

它会打印出以下内容:
false
true

返回的对象是NodeInfo类型,因此您需要将其包装为一个真正的Node,以便可以访问其方法:
Node n = NodeOverNodeInfo.wrap((NodeInfo) evaluate);
System.out.println(n.getNodeName());
System.out.println(n.getTextContent());

这个方法可行,是页面上最有帮助的答案。干得好! - james.garriss

2

Node 是一个接口。你需要有一个具体的类来实现它。而 getClass() 返回该具体类。

回应评论后的编辑:

对不起,我没有注意到 instanceof。看一下源代码,似乎 TinyNodeImpl 没有实现 org.w3c.dom.Node。看一下 JDK 文档,似乎也没有必要:对于 javax.xml.XPath 的文档引用了 XPathConstants 来获取结果类型,并引用了“XPath 1.0 NodeSet 数据类型”(如果你查看 XPath 1.0 规范,会发现这个类型并没有被定义)。

因此,从 XPath API 返回的内容只要在该 API 中使用时保持一致即可。我知道这不是你想听到的。你能使用内置的 JDK 实现吗?我知道它返回的是 org.w3c.dom 对象。


谢谢,我会澄清问题。是的,我得到了一个ClassCastException,你可以通过代码的最后两行看出来(evaluate instanceof Node),如果不是instanceof,它就没有实现那个接口,因此会发生类转换异常。 - Eran Medan

2
有点奇怪。 Saxon javadoc 表示 TinyElementImpl 没有实现任何 org.w3c.dom 接口,但您从 XPath 评估中得到了它们。
我猜 Saxon 放弃了标准 DOM 模型,而采用了自己的模型。我怀疑您在调用 evaluate 时传递给它的 XPathConstants.NODE 只是一个提示。XPath 表达式可以返回任何东西(例如,Apache JXPath 使用 XPath 表达式查询 Java 对象图),因此 Saxon 可以返回其自己的 DOM 类型,而不是标准的 org.w3c 类型。
解决方案:要么使用返回的 Saxon DOM 类型,要么不使用 Saxon。

-1

kdgregory是正确的,Node只是一个接口,而TinyElementImpl实现了该接口。 expression.evaluate()不能返回Node的实例,它必须返回一个实现了node的具体类。

指出你可以使用TinyElementImpl的一个实例作为Node,并且你可以轻松地将TinyElementImp的实例强制转换为Node

例如,这应该可以正常工作:

Node result = (Node) expression.evaluate(someXML, XPathConstants.NODE);

然后,您可以通过调用Node的任何方法,并将其传递给接受Node的任何方法来使用result


请阅读kdgregory答案中的注释。顺便说一下,TinyElementImpl没有实现Node接口。 - Eran Medan
好的,是的,我看到我做了一个错误的假设。不过,我认为我的回答精神上诚实地努力解决了问题的某些方面。虽然我可以接受负评。 - Avi Flax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接