xpath, how to select more than one item using indices - xpath

In this query, I select the 3rd
//tablecontainer/table/tbody/tr/td[3]
How do I select both the 3rd and 4th 's?

To get both the 3rd and 4th tds, you can use the expression:
//tablecontainer/table/tbody/tr/td[position() >= 3 and position() <= 4]

//tablecontainer/table/tbody/tr/td[position()=3 or position()=4]

If you can use XPath 2.0 you could use following trick
//tablecontainer/table/tbody/tr/td[position() = (1,2,4)]
Test position() = (1,2,4) means something similar as IN from SQL. Notice the brackets in (1,2,4) part.

Related

xPath union statement with functions

thanks in advance for reading.
I'm using $x() xPath evaluator of Chrome console.
I need to match something shaped like $x("e1 | e2").
In my case:
e1:
(//div[#class='seven columns omega']//form//div[#class='items_left']//text())[2]
e2
(substring-after(//div[#class='seven columns omega']//span[#class='sold_out']/text(),' - '))
They both works in the single way but if i want to combine them I just get stuck in
"Failed to execute 'evaluate'..."
PS The problem is e2 function substring-after, without it the union works.
Any Ideas?
Here are the 2 sources i'm trying to extract:
e1-case
e2-case
Thanks again :)
Based on your comments that you want string values and expect only one of those expressions to find something on the same page you could simply try
var result = $x("string((//div[#class='seven columns omega']//form//div[#class='items_left']//text())[2])") + $x("(substring-after(//div[#class='seven columns omega']//span[#class='sold_out']/text(),' - '))")

Select all nodes until a specific given node/tag

Given the following markup:
<div id="about">
<dl>
<dt>Date</dt>
<dd>1872</dd>
<dt>Names</dt>
<dd>A</dd>
<dd>B</dd>
<dd>C</dd>
<dt>Status</dt>
<dd>on</dd>
<dt>Another Field</dt>
<dd>X</dd>
<dd>Y</dd>
</dl>
</div>
I'm trying to extract all the <dd> nodes following <dt>Names</dt> but only until another <dt> starts. In this case, I'm after the following nodes:
<dd>A</dd>
<dd>B</dd>
<dd>C</dd>
I'm trying the following XPath code, but it's not working as intended.
xpath("//div[#id='about']/dl/dt[contains(text(),'Names')]/following-sibling::dd[not(following-sibling::dt)]/text()")
Any thoughts on how to fix it?
Many thanks.
Update: much simpler solution
There is a prerequisite in your situation, that is that the anchor item always is the first preceding sibling with a certain property. Because of that, here's a much simpler way of writing the below complex expression:
/div/dl/dd[preceding-sibling::dt[1][. = 'Names']]
In other words:
select any dd
that has a first preceding sibling dt (the preceding sibling axis counts backwards)
that itself has a value of "Names"
As can be seen in the following screenshot from oXygen, it selects the nodes you wanted to select (and if you change "Names" to "Status" or "Another Field", it will select only the following ones before the next dt also).
Original complex solution (leaving in for reference)
This is far easier in XPath 2.0, but let's assume you can only use XPath 1.0. The trick is to count the number of preceding siblings from your anchor element (the one with "Names" in it), and disregard any that have the wrong count (i.e., when we cross over <dt>Status</dt>, the number of preceding siblings has increased).
For XPath 1.0, remove the comments between (: and :) (in XPath, whitespace is insignificant, you can make it a multiline XPath for readability, but in 1.0, comments are not possible)
/div/dl/dd
(: any dd having a dt before it with "Names" :)
[preceding-sibling::dt[. = 'Names']]
(: count the preceding siblings up to dt with "Names", add one to include 'self' :)
[count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1
=
(: compare with count of all preceding siblings :)
count(preceding-sibling::dt)]
As a one-liner:
/div/dl/dd[preceding-sibling::dt[. = 'Names']][count(preceding-sibling::dt[. = 'Names']/preceding-sibling::dt) + 1 = count(preceding-sibling::dt)]
How about this:
//dd[preceding-sibling::dt[contains(., 'Names')]][following-sibling::dt]

XPath: how to select nodes by IN condition?

Is it possible to select nodes in a similar way?
'./tr[position() in (1, 3, 7)]'
I found only this solution:
'./tr[position() = 1 or position() = 3 or position() = 7]'
In XPath 2.0 you would simply do:
./tr[position = (1,3,7)]
In XPath 1.0 the usual way to do it is the solution you already found, an alternative that is a bit shorter would be something like:
./tr[contains('1 3 7', position())]
The spaces in the string are essential here, otherwise you'd also get nodes 13,37 and 137.

How can I select the second last item in a xpath query?

I'm new to xpath and I understand how to get a range of values in xpath:
/bookstore/book[position()>=2 and position()<=10]
but in my case, I need to get above 2 and one less then the total(so if there's 10 then I need 9, or if there's 5, I need up to the 4th spot). I'm applying my code to different pages and the number of entries is not always the same.
In python, I could do something like book[2:-2], but I'm unsure if I can do this within xpath.
You can use last() which represents the last item in the context:
/bookstore/book[position()>=2 and position() <= (last() - 1)]
In my case this was working for me to get last but one element
/bookstore/book[position() = (last() - 1)]

How to verify multiple values with xpath or store a value

My xpath expression is as follows:
//ep:getSwitchListResponse[1]/result[1]/item[position() >1 and position() <= last()]/ipAddress[1]/text()
I want to get the value of position() and use it to verify the name . something like
//ep:getSwitchListResponse[1]/result[1]/**item[Position()FromPreviousQuery**)]/name[1]/text()
Is this possible to do both in one xpath expression?
Unless you store the position from the first XPath somewhere locally, you'll have to recall the xpath again if you want to reuse the same position, XPath doesn't store anything from previous traversals.
Ex)
Your first xpath:
//ep:getSwitchListResponse[1]/result[1]/item[position() >1 and position() <= last()]/ipAddress[1]/text()
Its position:
count(//ep:getSwitchListResponse[1]/result[1]/item[position() >1 and position() <= last()]/ipAddress[1]/preceding-sibling::*)
The position XPath plugged into the second:
//ep:getSwitchListResponse[1]/result[1]/**item[count(//ep:getSwitchListResponse[1]/result[1]/item[position() >1 and position() <= last()]/ipAddress[1]/preceding-sibling::*)]/name[1]/text()

Resources