문제

I have the following code:

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

and this is the result...

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

Just to clarify, if I do this:

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

or

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

I get a ClassCastException

How can that be? according to Suns Java 1.5 API NODE and NODESET should map to org.w3c.dom.Node and org.w3c.dom.NodeList respectively

Just to clarify2 yes I know Node is an iterface, that getClass() returns a concrete class.

도움이 되었습니까?

해결책

Ok I figured it out!

If the evaluate method receives an InputSource the above error occurs.

e.g.

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

Then result is not implementing org.w3c.dom.Node (TinyElementImpl)

But if evaluate receives a Node (or a 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

It works, but still, this is weird...

다른 팁

Try this code:

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

It prints:

false
true

The returned object is of type NodeInfo, so you need wrap it as a real Node, so you can access its methods:

Node n = NodeOverNodeInfo.wrap((NodeInfo) evaluate);
System.out.println(n.getNodeName());
System.out.println(n.getTextContent());

It's a bit odd, this one. The Saxon javadoc says that TinyElementImpl doesn't implement any of the org.w3c.dom interfaces, and yet you're getting them back from the XPath evaluation.

My guess is that Saxon eschews the standard DOM model in favour of its own one. I suspect that the XPathConstants.NODE that you pass to evaluate is really just a hint. It's permitted for XPath expressions to return any old thing (for example, Apache JXPath uses XPath expressions to query java objects graphs), so it's permitted for Saxon to return its own DOM types rather than org.w3c standard ones.

Solution: either use the Saxon DOM types as returned, or don't use Saxon.

Node is an interface. You have to have a concrete class for implementation. And getClass() returns that concrete class.

Edit in response to comment:

Sorry, I didn't pay attention to the instanceof. Looking at the source code, it appears that TinyNodeImpl doesn't implement org.w3c.dom.Node. And looking at the JDK docs, it appears that it doesn't have to: the doc for javax.xml.XPath refers you to XPathConstants for the result type, and it refers to the "The XPath 1.0 NodeSet data type" (which, if you look at the XPath 1.0 spec, is not defined).

So, it seems that returns from the XPath API are only required to be consistent when used within that API. Not exactly what you wanted to hear, I'm sure. Can you use the built-in JDK implementation? I know that it returns org.w3c.dom objects.

kdgregory is correct that Node is just an interface, and TinyElementImpl implements that interface. expression.evaluate() can't return an instance of Node, it has to return a concrete class which implements node.

It might be useful to point out that you can use an instance of TinyElementImpl as as Node, and you can easily cast instances of TinyElementImp to Node.

For example, this should work just fine:

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

You can then use result by calling any of the methods of Node, and by passing it to any method which accepts a Node.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top