xPath: select following-sibling and self nodes - xpath

From this piece of HTML:
<tbody>
<tr>
<tr id="1" class="first">
<tr id="2" class="">
<tr id="3" class="last">
<tr id="4" class="first">
<tr id="5" class="">
<tr id="6" class="last">
<tbody>
I´m trying to get an xPath expression to return all tr[#class='first'] nodes followed by tr which don´t have a "first" class, including the first node. In this case a set with tr ids 1, 2, 3 and tr with ids 4, 5, 6.
I´ve tried using:
//tbody/tr[#class='first']/self::* | following-sibling::tr[not(starts-with(#class, 'first'))]
With no success. Any thoughts on this?
Thanks

While it's not exactly what you want, this expression will return all nodes between a first and last class, but it won't group them together. That's the best you'll be able to do with just one XPath expression.
Given an input like this,
<tbody>
<tr id="0" class=""/>
<tr id="1" class="first"/>
<tr id="2" class=""/>
<tr id="3" class="last"/>
<tr id="4" class="first"/>
<tr id="5" class=""/>
<tr id="6" class="last"/>
<tr id="7" class=""/>
</tbody>
This XPath expression,
/tbody/tr[
(following-sibling::tr[#class='last'] and preceding-sibling::tr[#class='first'])
or #class='first' or #class='last'
]
Will return these nodes:
<tr id="1" class="first" />
<tr id="2" class="" />
<tr id="3" class="last" />
<tr id="4" class="first" />
<tr id="5" class="" />
<tr id="6" class="last" />
The expression works by checking all tr elements whose preceding sibling is a 'first' and whose following sibling is a 'last'. That way it will exclude trs outside of those.
However, it does not do any grouping. I do not know of any way to do that kind of grouping with a single XPath expression. If I had to do it, I would have one XPath expression return all 'first' trs, then march through its siblings until I find 'last' tr. Maybe someone else knows of a better way to do this.

Related

Can't find element using normalize-space(text())

I'm having problems finding the following element with this xpath //label[normalize-space(text())='F. nacimiento:']
HTML DOM
The green element is the one I want to locate using the previous expression. I was able to find the red element with //label[normalize-space(text())='Servicio Decesos:'] and any other element of the same type.
I tried using //label[text()=' F. nacimiento:'] and works fine, but I'm using a loop and a the pattern //label[normalize-space(text())=' + foobar + '] to identify the rest of element.
My guess is something in that text string has a different char code to a whitespace or something like that, but I have no clue how to check that.
Is there a way to know what normalize-space is returning, in order to use it to id the element?
I have a different pattern to id the elements, but I'm pretty stubborn and this is a good chance to learn something new that could come handy in the future to solve similar issues.
I downloaded the html code as requested:
Here is the red fragment
<div class="sis-frame-bg">
<table class="wideBox" align="center" cellpadding="2" cellspacing="0">
<tbody>
<tr>
<th width="20%" align="right">
<label for="DTRIES_CODZONA">
<span class="sis-important">*</span> Código postal:</label>
</th>
<td><input name="nombdato_CODZONA_1" type="text" id="DTRIES_CODZONA" size="8" maxlength="8" onblur="seccionDecesos();" value="" data-regexp="_codigoPostal" autocomplete="off"></td>
</tr>
<tr>
<th width="20%" align="right">
<label for="DTRIES_SERVICOD">
<span class="sis-important">*</span> Servicio decesos:</label>
</th>
<td>
<select name="nombdato_SERVICOD_1" id="DTRIES_SERVICOD" onchange="seccionDescricionServicio();" data-name="Servicio decesos" data-obligatorio="true" data-regexp="_cadena">
<option selected="selected" value="" title="Elegir"> Elegir</option>
</select>
</td>
</tr>
<tr>
<th colspan="2" align="left">
<div id="datosServicioDecesos">
<!-- ASI261 -->
<!-- ASI261 -->
</div>
</th>
</tr>
</tbody>
</table>
</div>
And the green fragment
<table class="wideBox" cellspacing="0" cellpadding="2" align="center">
<tbody>
<tr>
<th align="right">
<label for="ASEG_FECHNACI">
<span class="sis-important">*</span> F. nacimiento:</label>
</th>
<td>
<input id="ASEG_FECHNACI" name="fechnaci" type="text" maxlength="10" size="12" class="js-popUpCalendar js-dateformat hasDatepicker" onblur="formatoFecha(this);" data-name="FECHA DE NACIMIENTO ASEGURADO" data-obligatorio="true" title="" value="" data-regexp="_fecha" autocomplete="off">
<img class="ui-datepicker-trigger" src="./ico_calendar.png" alt="..." title="...">
</td>
</tr>
</tbody>
</table>
You can use the below xpath.
//label[normalize-space(.)='* F. nacimiento:']
Screenshot

XPath syntax to make a query that exclude some specific element

You can find my test html page at https://sabbiobet.netsons.org/test.html
This is the html markup of the page:
<table border="1" class="class_table">
<tbody>
<tr class="class_tr">
<td class="class_td"> </td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ok"></span>square</td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ko"></span>circle</td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ok"></span>triangle</td>
</tr>
</tbody>
</table>
I need to obtain all the text in <td> with class="class_td" minus the ones that have text empty or or that have a child <span> with class="class_ko".
In other words I want to obtain only these values:
Square
Triangle
Using the importXML function of google sheets and following the suggestion of another user I've tried:
//td[#class='class_td' and span and not(span[#class='class_ko'])]
but it works only if i put some text between "span" and "/span"
Without any text I'll obtain only an empty result.
Can somebody help me?
In case the provided non-well-formed document is corrected to a well-formed one by replacing the undefined entity with the equivalent character entity reference  :
<table border="1" class="class_table">
<tbody>
<tr class="class_tr">
<td class="class_td"> </td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ok"></span>square</td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ko"></span>circle</td>
</tr>
<tr class="class_tr">
<td class="class_td"><span class="class_span_ok"></span>triangle</td>
</tr>
</tbody>
</table>
then this XPath expression:
/*/*/*/td
[#class='class_td'
and not(span[#class='class_span_ko'])
and normalize-space(translate(., ' ', ''))
]/>
when evaluated, selects exactly the wanted td elements:
<td class="class_td">
<span class="class_span_ok"/>square</td>
<td class="class_td">
<span class="class_span_ok"/>triangle</td>
XSLT - based verification
This transformation evaluates the above XPath expression and copies the selected elements to the output:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:copy-of select=
"/*/*/*/td
[#class='class_td'
and not(span[#class='class_span_ko'])
and normalize-space(translate(., ' ', ''))
]"/>
</xsl:template>
</xsl:stylesheet>
The wanted, correct result is produced:
<td class="class_td">
<span class="class_span_ok"/>square</td>
<td class="class_td">
<span class="class_span_ok"/>triangle</td>
Note:
If only the string values of the wanted elements are needed, then the XPath expression can be:
/*/*/*/td
[#class='class_td'
and not(span[#class='class_span_ko'])
and normalize-space(translate(., ' ', ''))
]/text()

Is it possible to use a xpath within a xpath?

I have a table that looks like this:
<table>
<thead>
<tr>
<th data-th-key="col1"> <span> Foo </span> </th>
<th data-th-key="col2"> <span> Bar </span> </th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div>
<input data-model-key="col1">
</div>
</td>
<td>
<div>
<input data-model-key="col2">
</div>
</td>
</tr>
</tbody>
</table>
To find the right input element I have to know the data-th-key from the table head. Is there any way to use the first xpath inside the second one?
Xpath one:
//table//thead//span[translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='foo']/parent::th/#data-th-key"
Xpath two:
//table//tbody//tr//td//div//input[#data-model-key='col1']
So I want to replace the col1 value in the second one with Xpath one.
You can write an expression like
//table//tbody//tr//td//div//input[#data-model-key = //table//thead//span[translate(normalize-space(.),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='foo']/parent::th/#data-th-key]
yes, that is possible and meaningful and selects the input (or the inputs) whose data-model-key is equal to that data-th-key attribute.

Preceding sibling return empty node

In this example i have this code:
<table class="basicinfo" cellspacing="0">
<tr class="header">
<td colspan="3">
<div>
<h2 class="prod_card">Basic info</h2>
</div>
</td>
</tr>
<tr class="row2 item">
<td class="cell0">
<div>
Year
</div>
</td>
<td class="cell1">
<div>
2005
</div>
</td>
</tr>
<tr class="row3 item alt">
<td class="cell0">
<div>
Extra
</div>
</td>
<td class="cell1">
<div>
-
</div>
</td>
</tr>
</table>
Now, i want to get (for example) the year. I'm trying to get the next div content after the div with Year content.
I'm using this xpath without success:
//div[preceding-sibling::div = 'Year']
And anyone knows a good website to start learning xpath? Thanks in advance!
Following XPath
//div[parent::td/preceding-sibling::td//div[normalize-space()= 'Year']]
has the result
<div>2005</div>
Your XPath didn't work because the div you are looking for has not a div element as preceding-sibling but a td as parent. The preceding-sibling::td of this td contains a div which has the text "Year" - td//div. Using normalize-space() returns the text of this div without any spaces which can be necessary when checking for equality.
For reference: normalize-space(), and as you asked for good resources - I don't want to recommend anything special, but you should have a look at the resources mentioned at the info given on stackoverflow here: https://stackoverflow.com/tags/xpath/info (if not already done) and, for XPath axes, this is a good visualization: http://www.xmlplease.com/axis

how to determine xpaths for ajax element

I need to detemine xpath for element mainForm:queryConfigure:fetchReport.
<span id="mainForm:queryConfigure:j_id18">
<table id="mainForm:queryConfigure:j_id19"
class="showReportTable" align="center">
<tbody>
<tr>
<td>
<input id="mainForm:queryConfigure:fetchReport" type="image"
src="images/show_report.gif" name="mainForm:queryConfigure:fetchReport"/>
</td>
</tr>
</tbody>
</table>
</span>
I tried
selenium.click("//input[#id='mainForm:queryConfigure:fetchReport'][#type='image'][#src='images/show_report.gif']");
and
selenium.click("//input[#id='mainForm:queryConfigure:fetchReport']");
One more case:
<div class="tabUnselectedText" align="center">
Notifications
</div>
Id and name attribute values are acceptable locators for method click. See locating elements in the documentation.
selenium.click('mainForm:queryConfigure:fetchReport');

Resources