Running XPath on child node

JavaXpath

Java Problem Overview


I'm trying to do a xpath lookup on nodes returned by xpath lookup, but it doesn't seem to work as I expected.XPaths executed on the child nodes of a document seem to be executd against hthe root node of the document (in the example, the inventory tag.), instead of the root of the provided node.

Am I missing something here? I'm new to XPath.

Also, please don't answer "just do //book[author='Neal Stephenson'/title". I have a legitimate use case, and this is a simplified example.

Code snippet

DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse("src/main/java/books.xml");

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

Node book = (Node) xpath.evaluate("//book[author='Neal Stephenson']", doc, XPathConstants.NODE);
Node title = (Node) xpath.evaluate("/title", book, XPathConstants.NODE); // I get null here.
Node inventory = (Node) xpath.evaluate("/inventory", book, XPathConstants.NODE); // this returns a node.

book.xml

<inventory>
<book year="2000">
	<title>Snow Crash</title>
	<author>Neal Stephenson</author>
	<publisher>Spectra</publisher>
	<isbn>0553380958</isbn>
	<price>14.95</price>
</book>

<book year="2005">
	<title>Burning Tower</title>
	<author>Larry Niven</author>
	<author>Jerry Pournelle</author>
	<publisher>Pocket</publisher>
	<isbn>0743416910</isbn>
	<price>5.99</price>
</book>

<book year="1995">
	<title>Zodiac</title>
	<author>Neal Stephenson</author>
	<publisher>Spectra</publisher>
	<isbn>0553573862</isbn>
	<price>7.50</price>
</book>

<!-- more books... -->

</inventory>

Java Solutions


Solution 1 - Java

/foo will select based off of the root node, ignoring the context that you are evaluating the xpath against. foo (without the slash) is what you want; that selects based off of the current node.

https://www.w3schools.com/xml/xpath_syntax.asp gives a bit more info.

Solution 2 - Java

in Xpath, "." (Dot) represents the current document. So, write your XPATH string after a "." (Dot) .

ex :

"./title"

or

".//title"

Whatever you want....

removing the slash works only if its a child of the node. What if you want to use the // (wherever in the current Document) functionality ?

So, use the dot (.)

Thanks a lot for the above answers too :) .

Solution 3 - Java

Just take the leading slash off of your subqueries and you should be fine. So you get your books via "//book", and then from there it's just "title", "inventory" etc to get the child bits.

Solution 4 - Java

What is actually weird in the Java implementation is that a Node extracted from a Document still references the parent Document (see Node.getOwnerDocument()) and xpath uses this to find the root.

Others have mentioned a way to modify the xpath to actually not start from the root by removing the slashes.

I had a similar issue but I wanted the xpath to handle both root documents and child nodes (with an xpath like /title). The solution was to clone the node : Node.cloneNode(true). Note the true parameter that makes the Node shake its parent Document off.

...In the end, it hurts performance too much and having separate xpaths to handle Node and Document was preferred.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeeyoung KimView Question on Stackoverflow
Solution 1 - JavaPete HodgsonView Answer on Stackoverflow
Solution 2 - JavaSalmanView Answer on Stackoverflow
Solution 3 - JavaCorey PorterView Answer on Stackoverflow
Solution 4 - JavamauhizView Answer on Stackoverflow