1

Given:

public class XPathTest {

    public static void main(String args[]) throws Exception {
        String xmlString
                = "<a>"
                + "<b c=\"1\"/>"
                + "<b c=\"2\"/>"
                + "</a>";

        ByteArrayInputStream bis = new ByteArrayInputStream(xmlString.getBytes());
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = domFactory.newDocumentBuilder();
        Document doc = builder.parse(new InputSource(bis));

        XPathFactory factory = XPathFactory.newInstance();
        XPath xpath = factory.newXPath();
        XPathExpression expr = xpath.compile("//b");

        NodeList nl = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
        dumpNodeList(nl);
        for (int i = 0; i < nl.getLength(); i++) {
            Node n = nl.item(i);
            NodeList nl2 = (NodeList) expr.evaluate(n, XPathConstants.NODESET);
            dumpNodeList(nl2);
        }

    }

    public static void dumpNodeList(NodeList nl) {
        System.out.println("NodeList length = " + nl.getLength());
        for (int i = 0; i < nl.getLength(); i++) {
            System.out.println("Node #" + i);
            Element e = (Element) nl.item(i);
            System.out.println("Name = " + e.getTagName());
            System.out.println("Attr = " + e.getAttribute("c"));
        }
        System.out.println();
    }
}

Sample result:

NodeList length = 2
Node #0
Name = b
Attr = 1
Node #1
Name = b
Attr = 2

NodeList length = 2
Node #0
Name = b
Attr = 1
Node #1
Name = b
Attr = 2

NodeList length = 2
Node #0
Name = b
Attr = 1
Node #1
Name = b
Attr = 2

I evaluate the XPath expression //b, passing in the root document node to the evaluator. It, expectedly, returns the two b nodes.

What I'm confused about is when I evaluate the same expression, but instead of passing in the root node as he parameter I pass in one of the children nodes I located earlier. According to XPathExpression.evaluate(Object item), it says Evaluate the compiled XPath expression in the specified context and return the result as the specified type.

The expression, //b, means "give me all of the b nodes".

Intuitively, I would think that if I pass in a Node to XPathExpression.evalute(Object item), that the expression would be evaluated starting with that node as its root for the "give me all" part, rather than the entire document. So I would expect a resulting NodeList of one node, not two.

But instead, I get the same two nodes as if from the entire document.

So, the two questions are:

  1. Why is the expression being evaluated relative to the entire document, and not to just using the pass Node as a synthetic root for the evaluation?

  2. How can I get the expression to evaluate using the passed in Node as the synthetic root for the evaluation?

Will Hartung
  • 115,893
  • 19
  • 128
  • 203

2 Answers2

3

It works as it should.. Instead of //b, please try with ./b. Maybe M$ help is not very helpful, but here they have some handy examples.

Michał Zaborowski
  • 3,911
  • 2
  • 19
  • 39
  • Thank you, this was very helpful. //b returns all b nodes from the entire document. ./b would return all the b nodes immediately within the current context. .//b would be all b nodes within the context, regardless of their depth, but not the root node (if it happens to be a b node). When I'm iterating across the b nodes that I found, ./b will not work (as the context IS the b node). However, this was the insight I needed to solve my problem. – Will Hartung Nov 16 '17 at 00:25
3

An expression starting with /, like //b, starts by navigating upwards from the context node to the root of the containing tree. So your cited documentation is correct that it is "evaluated in the current context", but you somehow misread this as saying that it only looks at the subtree rooted at the context node. If you choose to navigate upwards from the context node, you can, and that is precisely what you have done.

You probably wanted .//b.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Yea, the idea of something walking to the root of the context, and then searching, was not intuitive to me. – Will Hartung Nov 16 '17 at 00:28
  • It was designed on the assumption that most programmers are familiar with filenames and URIs, which work in the same way. – Michael Kay Nov 16 '17 at 09:18