Why is XPath last() function not working as I expect?
Solution 1
This is a common source of XPath confusion. First the straightforward parts:
//a
selects alla
elements in the document.//a//b
selects allb
elements in the document that are descendants ofa
elements.
Normal stuff so far. Next is the tricky part:
To select the last
b
elements among siblings (beneatha
elements)://a//b[last()]
Here, the filtering is a part of the
b
selection criteria because[]
has a higher precedence than//
.To select the last
b
element in the document (beneatha
elements):(//a//b)[last()]
Here, the
last()
is an index on the list of all selectedb
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.
LoveLovelyJava one
Updated on July 27, 2022Comments
-
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 about 8 yearsit worked but could you explain a bit more, I'm quite confused :(
-
LoveLovelyJava one about 8 years
-
kjhughes about 8 yearsYes, but
//a//b[last()]
and(//a//b)[last()]
do not select the same. Answer updated with an expanded explanation. -
LoveLovelyJava one about 8 yearsso 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 about 8 yearsjust one more question, where should i go to learn the exact behavior of these functions? do you know any online source?
-
kjhughes about 8 yearsThe original XPath Recommendation is remarkably readable. Don't miss the location path examples
-
kjhughes about 8 yearsXSLT 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 about 8 yearsThanks indeed for your time and help:)
-
LoveLovelyJava one about 8 yearsI 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 about 8 yearsYes, that statement is correct, but my and Michael Kay's answers make a more subtle point than that.