Jeeyoung Kim
Jeeyoung Kim

Reputation: 6098

Running XPath on child node

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>

Upvotes: 67

Views: 51395

Answers (4)

Pete Hodgson
Pete Hodgson

Reputation: 15845

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

Upvotes: 66

Salman
Salman

Reputation: 371

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

Upvotes: 37

mauhiz
mauhiz

Reputation: 501

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.

Upvotes: 6

Corey Porter
Corey Porter

Reputation: 1339

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.

Upvotes: 8

Related Questions