3

First, I must say that I find Xpath as a very nice parser , and I guess pretty powerful when comparing it to other parsers .

Given the following code :

  DocumentBuilderFactory domFactory = 
  DocumentBuilderFactory.newInstance();
  domFactory.setNamespaceAware(true); 
  DocumentBuilder builder = domFactory.newDocumentBuilder();
  Document doc = builder.parse("input.xml");
  XPath xpath = XPathFactory.newInstance().newXPath();

If I wanted to find the first node of Round 1 & Door 1 , here :

<Game>
    <Round>
        <roundNumber>1</roundNumber>
        <Door>
            <doorName>abd11</doorName>
            <Value>
                <xVal1>0</xVal1>
                <xVal2>25</xVal2>
                <pVal>0.31</pVal>
            </Value>
            <Value>
                <xVal1>25</xVal1>
                <xVal2>50</xVal2>
                <pVal>0.04</pVal>
            </Value>
            <Value>
                <xVal1>50</xVal1>
                <xVal2>75</xVal2>
                <pVal>0.19</pVal>
            </Value>
            <Value>
                <xVal1>75</xVal1>
                <xVal2>100</xVal2>
                <pVal>0.46</pVal>
            </Value>
        </Door>
        <Door>
            <doorName>vvv1133</doorName>
            <Value>
                <xVal1>60</xVal1>
                <xVal2>62</xVal2>
                <pVal>1.0</pVal>
            </Value>
        </Door>
    </Round>
    <Round>
        <roundNumber>2</roundNumber>
        <Door>
            <doorName>eee</doorName>
            <Value>
                <xVal1>0</xVal1>
                <xVal2>-25</xVal2>
                <pVal>0.31</pVal>
            </Value>
            <Value>
                <xVal1>-25</xVal1>
                <xVal2>-50</xVal2>
                <pVal>0.04</pVal>
            </Value>
            <Value>
                <xVal1>-50</xVal1>
                <xVal2>-75</xVal2>
                <pVal>0.19</pVal>
            </Value>
            <Value>
                <xVal1>-75</xVal1>
                <xVal2>-100</xVal2>
                <pVal>0.46</pVal>
            </Value>
        </Door>
        <Door>
            <doorName>cc</doorName>
            <Value>
                <xVal1>-60</xVal1>
                <xVal2>-62</xVal2>
                <pVal>0.3</pVal>
            </Value>
            <Value>
                <xVal1>-70</xVal1>
                <xVal2>-78</xVal2>
                <pVal>0.7</pVal>
            </Value>
        </Door>
    </Round>
</Game>

I'll do this :

 XPathExpression expr = xpath.compile("//Round[1]/Door[1]/Value[1]/*/text()");      
  Object result = expr.evaluate(doc, XPathConstants.NODESET);
  NodeList nodes = (NodeList) result;

and if I wanted the second node of Round 1 & Door 1 then :

XPathExpression expr = xpath.compile("//Round[1]/Door[1]/Value[2]/*/text()");  

but how do I do this using a loop , since I don't know how much Value-nodes I have , meaning how can I do this using a loop , where each iteration I retrieve 3 (I mean the xVal1 , xVal2 and pVal values ) more values of a Value node !?

The reasons for asking for this are :

  1. I don't know how much Round-s I have

  2. I don't know how much Value-s I have

  3. I don't want to declare every time a new XPathExpression

Thanks .

5
  • Use the XPath count() function to find the number of nodes, then use a (nested) loop: Commented May 5, 2012 at 17:08
  • Why not several expressinos? Isn't that what you want (e.g. first get list of Rounds, then Doors, then Values?) Commented May 5, 2012 at 17:18
  • @erikxiv : The problem is that each Round has 2 fields , one is "roundNumber" and the second is "Door" . Now with a single query , i.e. XPathExpression , I can only get either but not both ? because from what I see at the moment , I have to do 2 queries - one for the field "roundNumber" and another for the "Door" of each Round ... and so on Commented May 5, 2012 at 17:49
  • @DimitreNovatchev: I've tried count but it doesn't work , take a look : if I write this : "double count = (Double) xpath.evaluate("count(//Round/Door[1]/Value)", doc, XPathConstants.NUMBER);" then I get "count == 8" ......... if I write instead "int k = 1; double count = (Double) xpath.evaluate("count(//Round/Door[k]/Value)", doc, XPathConstants.NUMBER);" , then I get "count == 0" Commented May 5, 2012 at 18:31
  • 2
    @ron: This is one of the most common XPath FAQ: use: count((//Round/Door)[1]/Value) and then count((//Round/Door)[$k]/Value) Commented May 5, 2012 at 19:59

1 Answer 1

7

Option 1 - Iterate over all Value elements in the document. Only one evaluation required, but difficult to know which Round or Door element the Value belongs to.

NodeList result = (NodeList) xpath.evaluate("//Round/Door/Value/*/text()", doc, XPathConstants.NODESET);

Option 2 - Iterate over each Round, Door and Value elements separately. Requires more evaluations but the context is easily known. If index is required, it is easy to add a counter to the loops.

// Get all rounds and iterate over them
NodeList rounds = (NodeList) xpath.evaluate("//Round", doc, XPathConstants.NODESET);
for (Node round : rounds) {
  // Get all doors and iterate over them
  NodeList doors = (NodeList) xpath.evaluate("Door", round, XPathConstants.NODESET);
  for (Node door : doors) {
    // Get all values and iterate over them
    NodeList values = (NodeList) xpath.evaluate("Value/*/text()", door, XPathConstants.NODESET);
    for (Node value : values) {
      // Do something
    }
  }
}

Option 3 - Do some combination of the above depending on your requirements

Note that I've removed the expression compilation step to shorten the example. It should be re-added to improve performance.

Sign up to request clarification or add additional context in comments.

3 Comments

Which NodeList class are you using that implements Iterable?
@majorbanzai Are you thinking about the enhanced for-loops syntax? I suspect you are right in that the code sample won't work as NodeList is not an Iterable. Does make the example more readable though...
Nodelist cannot be iterated like that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.