Why is XPath last() function not working as I expect?

11,101

Solution 1

This is a common source of XPath confusion. First the straightforward parts:

  • //a selects all a elements in the document.
  • //a//b selects all b elements in the document that are descendants of a elements.

Normal stuff so far. Next is the tricky part:

  1. To select the last b elements among siblings (beneath a elements):

    //a//b[last()]
    

    Here, the filtering is a part of the b selection criteria because [] has a higher precedence than //.

  2. To select the last b element in the document (beneath a elements):

    (//a//b)[last()]
    

    Here, the last() is an index on the list of all selected b elements because () is used to override the default precedence.

Solution 2

I think it's easiest to understand the behaviour if you remember that "//" is an abbreviation for "/descendant-or-self::node()/", and that the step "b" is an abbreviation for "child::b". So

//b[last()]

is an abbreviation for

/descendant-or-self::node()/child::b[position()=last()]

Which means "Select every node in the document (except attributes and namespaces). For each of these nodes, form a list of the child elements named "b", and select the last element in this list".

You ask for sources of information. @kjhughes recommends reading the XPath 1.0 recommendation, and indeed, it is a lot more readable than many specs. But it can be a bit terse at times; it occasionally feels like solving a crossword puzzle. My "XSLT 2.0 Programmer's Reference" (which also includes a lot of material on XPath) was written for people who want a deep understanding of how the language works, but explained in plainer English. This particular topic is on page 627, and it's easy enough to find a pirated copy on the web if you want to see how it's covered. But I'd recommend buying a legal copy, because scrolling through 1300 pages of scanned PDF is not much fun.

Share:
11,101
LoveLovelyJava one
Author by

LoveLovelyJava one

Updated on July 27, 2022

Comments

  • LoveLovelyJava one
    LoveLovelyJava one almost 2 years

    I am using Java and Selenium to write a test. I need to get the last element inside another element, so I used last() function, but the problem is that it doesn't always bring me the last one when I apply :

    //a//b[last()]
    

    to

     <a> 
       <l>
         <b>asas</b> 
       </l>
       <b>as</b>
     </a> 
    

    to get <b>as</b> ,it brings me:

    <b>asas</b>
    
    <b>as</b>
    

    but when I apply it to:

     <a>      
       <b>asas</b> 
       <b>as</b>
     </a>
    

    it brings me:

    <b>as</b>
    
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    it worked but could you explain a bit more, I'm quite confused :(
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    either of //a//b and (//a//b) have the same result when I check them at link
  • kjhughes
    kjhughes about 8 years
    Yes, but //a//b[last()] and (//a//b)[last()] do not select the same. Answer updated with an expanded explanation.
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    so that is why when I apply //a//b[last()] to <a><l><b>as</b><b>asasas</b></l><b>asas</b></a> I get <b>asasas</b> <b>asas</b> makes sense now, thanks
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    just one more question, where should i go to learn the exact behavior of these functions? do you know any online source?
  • kjhughes
    kjhughes about 8 years
    The original XPath Recommendation is remarkably readable. Don't miss the location path examples
  • kjhughes
    kjhughes about 8 years
    XSLT 2.0 Programmer's Reference is available from the publisher here or Amazon here Buy it. As developers, the cost is nothing compared with the time understanding it will save you. Michael Kay is extremely generous with his time here and across the net, btw, so you can see how well he writes on complex topics; his book is even better.
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    Thanks indeed for your time and help:)
  • LoveLovelyJava one
    LoveLovelyJava one about 8 years
    I found this explanation: /bookstore/book[last()] Selects the last book element that is the child of the bookstore element from w3schools is it the same as what you said, or are they wrong?
  • kjhughes
    kjhughes about 8 years
    Yes, that statement is correct, but my and Michael Kay's answers make a more subtle point than that.