views:

478

answers:

5

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.

A: 

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
Thanks, I'll clarify the question. And yes, I get a ClassCastException, you can tell that by the last 2 lines of the code (evaluate instanceof Node) if it is not instanceof, it doesn't implement that interface, thus a class cast exceptino will occur.
Ehrann Mehdan
A: 

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.

Avi Flax
Please read the comment on kdgregory's answer. TinyElementImpl doesn't implement Node by the way.
Ehrann Mehdan
OK, yeah, I see that I made an incorrect assumption.Still, I think the spirit of my answer does address some aspects of the question in an honest effort to help.I can live with the downvote though.
Avi Flax
+1  A: 

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.

skaffman
+1  A: 

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...

Ehrann Mehdan
Makes sense.... if you put W3C DOM in, you get W3C DOM out. Otherwise, you get proprietary DOM out.
skaffman
Welcome to Java XML.
Esko