I want to select one html element based upon the position of another using xpath. For example:
<table>
<tr>
<th>
Col1
</th>
<th>
Col2
</th>
<th>
Col3
</th>
<th>
Col4
</th>
</tr>
<tr>
<td>
Value1
</td>
<td>
Value2
</td>
<td>
Value3
</td>
<td>
Value4
</td>
</tr>
</table>
In this example I want the td which is in the same position in the collection of tds that the th with the contents Col2 is.
I can find the position of the th
//th[contains(.,'Col2')]
I want to avoid doing this
//td[2]
Is there any way I can link the two?
This is one possible way :
//td[
position() = count(//th[contains(.,'Col2')]/preceding-sibling::th)+1
]
xpathtester demo
The XPath returns td at position equals to position of the th. position of the th is calculated by counting number of preceding sibling th, +1 since XPath position index starts from 1.
Related
I have a table for comments, each comment could have a decision from a dropdown.
If the specific decision is selected the user then could select one/many services.
If a specific service is selected only one grade is selected.
So the result comments section could look like these examples:
This is a comment without a decision
This is a comment with a decision
decision: This is decision 1
This is a comment with a decision and services
decision: This is decision 2
services: ['This is service 1', 'This is service 2']
This is a comment with a decision, services, and grade
decision: This is decision 2
services: ['this is service 1', 'This is service 3 grade: 2']
The decision is only a single selection. The services are multiple checkboxes. The grade is only a single selection.
The form looks like that:
<form>
<textarea name="comment"></textarea>
<select name="decision">
<option value="1">Decision 1</option>
<option value="2">Decision 2</option>
</select>
<!-- Show if decision 2 is selected -->
<div class="services">
<input type="checkbox" name="services[]" value="1"/>
<input type="checkbox" name="services[]" value="2"/>
</div>
<!-- Show if service 2 is checked -->
<select name="grade">
<option value="1">1</option>
<option value="2">2</option>
</select>
</form>
DB structure:
comments table:
id | comment
1 | This is comment 1
2 | This is comment 2
decisions table:
id | decision
1 | This is decision 1
2 | This is decision 2
services table:
id | service
1 | This is service 1
2 | This is service 2
grades table:
id | grade
1 | 1
2 | 2
3 | 3
4 | 4
Finally, these tables are related using 3 tables:
comment_decisions table:
id | decision_id | comment_id
comment_services table:
id | service_id | comment_id
comment_grades table:
id | comment_services_id | grade_id
I did that to avoid duplication and NULL values in the case of one table:
id | comment | decision_id (nullable) | service_id (nullable) | grade_id (nullable)
This issue is when I try to update the decision, services, and grade. When I use updateOrCreate new records are created not updated. attach and sync not working either.
Is there a better structure or I'm missing the right functions to update the tables?
I have next table sturcture:
<table>
<thead>
<tr>
<th>User Role</th>
<th>Inactive</th>
<th>Admin Area</th>
<th>Need Confirm</th>
</tr>
</thead>
<tbody>
<tr>
<td>Admin</td>
<td>False</td>
<td>True</td>
<td>False</td>
</tr>
<tr>
<td>Member</td>
<td>False</td>
<td>False</td>
<td>True</td>
</tr>
</tbody>
</table>
How can I select a cell according to column name and row from first column (e.g. select Admin Area cell value for Member)?
If you can guarantee that User Role is the first column, then the following will work:
//td[count(//th[text()="Admin Area"]/preceding-sibling::*)+1][../td[1]="Member"]
Explanation:
count(//th[text()="Admin Area"]/preceding-sibling::*)+1 gives you the index of "Admin Area" among the table headers, in this case 3
//td[3] selects all td cells in the third column
//[../td[1]="Member"] further qualifies this by selecting only the td cells whose sibling in the first column contains "Member"
I have field in a Oracle database of type CLOB. I extract the first line alone from this CLOB filed. Here's an example of the content:
"<div class="pi-tier3"><div class="pi-pdpmainbody"><p><b>FIT</b></p><p>Core Indy - Compression </p>
<p><b>PRO</b></p><ul>
<li>ABCDEF:PmId12345RmLn1VlId0</li>
<li>ABCDEF:PmId12345RmLn1VlId0</li>
<li>ABCDEF:PmId12345RmLn1VlId0</li>
<li></li>
<li>ABCDEF:PmId12345RmLn1VlId0</li>
</ul>
<p><b>PRP</b></p><ul>
<li>100%</li>
<li>DRY</li>
</ul>
<p>ABCDEF:PmId12345RmLn1VlId0</p>
</div></div>"
The result should look like this:
"<div class="pi-tier3"><div class="pi-pdpmainbody"><p><b>FIT</b></p><p>Core Indy - Compression </p>
If you truly want to return the characters from the beginning of the clob value to the characters before the first newline (chr 10), then here is an example:
create table tab1 (c1 clob);
insert into tab1 values ('line 1' || chr(10) || 'line 2');
select dbms_lob.substr(c1, dbms_lob.instr(c1, chr(10))-1, 1) from tab1;
DBMS_LOB.SUBSTR(C1,DBMS_LOB.INSTR(C1,CHR(10))-1,1)
------------------------------------------------------------------------------
line 1
Check this out for more information.
I would do that using REGEXP_SUBSTR, like so:
select
REGEXP_SUBSTR(
'<div class="pi-tier3"><div class="pi-pdpmainbody"><p><b>FIT</b></p><p>Core Indy - Compression </p>
<p><b>PRO</b></p><ul>'
, '^.*$', 1, 1, 'm')
from dual;
Cheers
I'm trying to parse some data that is stored in a Hive table.
Let's say.
Hive> SELECT * FROM fulldatatable LIMIT 1;
SELECT xpath( 'xml data from the previous command') SELECT src LIMIT 1;
My question is how to load the first in the xpath query?
Thanks,
You may create a view from the first select, and then query this one with the xpath UDF. E.g:
Initial tables:
hive> describe table1;
id int
f1 string
f2 string
f3 string
f4 string
hive> select * from table1;
1 <a> <b>1</b> <b>1</b> </a>
2 <a> <b>1</b> <b>2</b> </a>
3 <a> <b>1</b> <b>3</b> </a>
Another table:
hive> describe ranks;
id int
text string
hive> select * from ranks;
1 good
2 bad
3 worst
Create a view:
hive> create view xmlout(id, line) as select id, concat(f1,f2,f3,f4) from table1;
Then:
hive> select xpath_short(x.line, 'sum(a/b)'), r.text from xmlout x
join ranks r on (x.id = r.id);
2 good
3 bad
4 worst
I'm writing a Selenium testcase. And here's the xpath expression I use to match all 'Modify' buttons within a data table.
//img[#title='Modify']
My question is, how can I visit the matched node sets by index? I've tried with
//img[#title='Modify'][i]
and
//img[#title='Modify' and position() = i]
But neither works..
I also tried with XPath checker(One firefox extension). There're totally 13 matches found, then I have totally no idea how am I gonna select one of them..
Or does XPath support specified selection of nodes which are not under same parent node?
This is a FAQ:
//someName[3]
means: all someName elements in the document, that are the third someName child of their parent -- there may be many such elements.
What you want is exactly the 3rd someName element:
(//someName)[3]
Explanation: the [] has a higher precedence (priority) than //. Remember always to put expressions of the type //someName in brackets when you need to specify the Nth node of their selected node-list.
There is no i in XPath.
Either you use literal numbers: //img[#title='Modify'][1]
Or you build the expression string dynamically: '//img[#title='Modify']['+i+']' (but keep in mind that dynamic XPath expressions do not work from within XSLT).
Or does XPath support specified selection of nodes
which are not under same parent node?
Yes: (//img[#title='Modify'])[13]
This //img[#title='Modify'][i] means "any <img> with a title of 'Modify' and a child element named <i>."
There is no i in xpath is not entirely true. You can still use the count() to find the index.
Consider the following page
<html>
<head>
<title>HTML Sample table</title>
</head>
<style>
table, td, th {
border: 1px solid black;
font-size: 15px;
font-family: Trebuchet MS, sans-serif;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
text-align: left;
padding: 8px;
}
tr:nth-child(even){background-color: #f2f2f2}
th {
background-color: #4CAF50;
color: white;
}
</style>
<body>
<table>
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<th>Heading 4</th>
<th>Heading 5</th>
<th>Heading 6</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data row 1 col 1</td>
<td>Data row 1 col 2</td>
<td>Data row 1 col 3</td>
<td>Data row 1 col 4</td>
<td>Data row 1 col 5</td>
<td>Data row 1 col 6</td>
</tr>
<tr>
<td>Data row 2 col 1</td>
<td>Data row 2 col 2</td>
<td>Data row 2 col 3</td>
<td>Data row 2 col 4</td>
<td>Data row 2 col 5</td>
<td>Data row 2 col 6</td>
</tr>
<tr>
<td>Data row 3 col 1</td>
<td>Data row 3 col 2</td>
<td>Data row 3 col 3</td>
<td>Data row 3 col 4</td>
<td>Data row 3 col 5</td>
<td>Data row 3 col 6</td>
</tr>
<tr>
<td>Data row 4 col 1</td>
<td>Data row 4 col 2</td>
<td>Data row 4 col 3</td>
<td>Data row 4 col 4</td>
<td>Data row 4 col 5</td>
<td>Data row 4 col 6</td>
</tr>
<tr>
<td>Data row 5 col 1</td>
<td>Data row 5 col 2</td>
<td>Data row 5 col 3</td>
<td>Data row 5 col 4</td>
<td>Data row 5 col 5</td>
<td>Data row 5 col 6</td>
</tr>
<tr>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
</tr>
</tbody>
</table>
</br>
<table>
<thead>
<tr>
<th>Heading 7</th>
<th>Heading 8</th>
<th>Heading 9</th>
<th>Heading 10</th>
<th>Heading 11</th>
<th>Heading 12</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data row 1 col 1</td>
<td>Data row 1 col 2</td>
<td>Data row 1 col 3</td>
<td>Data row 1 col 4</td>
<td>Data row 1 col 5</td>
<td>Data row 1 col 6</td>
</tr>
<tr>
<td>Data row 2 col 1</td>
<td>Data row 2 col 2</td>
<td>Data row 2 col 3</td>
<td>Data row 2 col 4</td>
<td>Data row 2 col 5</td>
<td>Data row 2 col 6</td>
</tr>
<tr>
<td>Data row 3 col 1</td>
<td>Data row 3 col 2</td>
<td>Data row 3 col 3</td>
<td>Data row 3 col 4</td>
<td>Data row 3 col 5</td>
<td>Data row 3 col 6</td>
</tr>
<tr>
<td>Data row 4 col 1</td>
<td>Data row 4 col 2</td>
<td>Data row 4 col 3</td>
<td>Data row 4 col 4</td>
<td>Data row 4 col 5</td>
<td>Data row 4 col 6</td>
</tr>
<tr>
<td>Data row 5 col 1</td>
<td>Data row 5 col 2</td>
<td>Data row 5 col 3</td>
<td>Data row 5 col 4</td>
<td>Data row 5 col 5</td>
<td>Data row 5 col 6</td>
</tr>
<tr>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
<td><button>Modify</button></td>
</tr>
</tbody>
</table>
</body>
</html>
The page has 2 tables and has 6 columns each with unique column names and 6 rows with variable data. The last row has the Modify button in both the tables.
Assuming that the user has to select the 4th Modify button from the first table based on the heading
Use the xpath //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button
The count() operator comes in handy in situations like these.
Logic:
Find the header for the Modify button using //th[.='Heading 4']
Find the index of the header column using count(//tr/th[.='Heading 4']/preceding-sibling::th)+1
Note: Index starts at 0
Get the rows for the corresponding header using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]
Get the Modify button from the extracted node list using //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button
//img[#title='Modify'][i]
is short for
/descendant-or-self::node()/img[#title='Modify'][i]
hence is returning the i'th node under the same parent node.
You want
/descendant-or-self::img[#title='Modify'][i]
(//*[#attribute='value'])[index]
to find target of element while your finding multiple matches in it
Here is the solution for index variable
Let's say, you have found 5 elements with same locator and you would like to perform action on each element by providing index number (here, variable is used for index as "i")
for(int i=1; i<=5; i++)
{
string xPathWithVariable = "(//div[#class='className'])" + "[" + i + "]";
driver.FindElement(By.XPath(xPathWithVariable)).Click();
}
It takes XPath :
(//div[#class='className'])[1]
(//div[#class='className'])[2]
(//div[#class='className'])[3]
(//div[#class='className'])[4]
(//div[#class='className'])[5]