Search code examples
xpathxslt

XPath square bracket operator produces unexpected result


Consider this XML document:

<foo>
    <bar id="aaa"/>
    <jam>
        <bar id="bbb"/>
    </jam>
</foo>

I am trying to find the first <bar> element in the document.

Here's what I've discovered:

Test# XPath Expression Evaluation Expression Result
1 //bar @id aaa, bbb
2 //bar[1] @id aaa, bbb
3 (//bar)[1] @id aaa

What I don't understand is why Test #2 returns aaa, bbb instead of just aaa. It's as if the [1] operator is not working.

My understanding was that in Test #2, the //bar expression would find both nodes, and then the [1] operator would choose the first one. Obviously, I'm missing something.

This seems to be confirmed by Test #3 which is apparently how to express what I really want.

Also, if you change the XML document to this:

<foo>
    <bar id="aaa"/>
    <bar id="bbb"/>
</foo>

then you get what I was expecting to see:

Test# XPath Expression Evaluation Expression Result
1 //bar @id aaa, bbb
2 //bar[1] @id aaa
3 (//bar)[1] @id aaa

What subtlety am I missing here about how [1] is supposed to work?


Solution

  • The XPath specification contains this note regarding the // abbreviated syntax:

    Note:
    The path expression //para[1] does not mean the same as the path expression /descendant::para[1]. The latter selects the first descendant para element; the former selects all descendant para elements that are the first para children of their respective parents.

    IOW, if you want to select the first bar element in the document you need to use:

    /descendant::bar[1]
    

    instead of:

    //bar[1]
    

    Unlike the other answers, I am not claiming it makes sense.