xpath get last of specific preceding-sibling

24,464

Solution 1

I do not understand what the problem exactly is, but preceding-siblings are sorted from the node itself towards the beginning of the document, i.e. the other way round than in the document. To get the nearest preceding sibling, use preceding-sibling[1], to get the farthest one (i.e. the first one in the document order), use preceding-sibling[last()].

Solution 2

After reading your update, wouldn't this work:

//cell[control/id="MyItemId_4"]/preceding-sibling::cell[control[@xsi:type='Label']  and not(control/name='')][1]

I'm a bit unsure about the name node: do you want to test for existence of text in the the name node or just existence of the name node itself?

Solution 3

YourWebElement.FindElement(By.XPath("preceding-sibling::*[1]"));

Here 1 indicates just the sibling above the selected node and then you can do recursion in order to get all the preceding siblings from bottom to top.

Share:
24,464
Oliver Friedrich
Author by

Oliver Friedrich

Updated on July 21, 2022

Comments

  • Oliver Friedrich
    Oliver Friedrich almost 2 years

    Here's something really simple (at least I guess), I just do not get the clue.

    I have to parse a large XML document to get a specific node, identified by one of its subnode values. Thats easy so far. But when I try to parse onward from that node relatively upward, selecting the preceding-siblings of its ancestor by using a predicate I get a list of nodes, from that on I have to walk downward again.

    In Theorie, that is a table, with 5 columns and two rows (in the shown example below). I get just the id element of one field, and need to find the name given in the first field of the row. The first field is always of type 'Link' and has a name subnode with text - which is the thing to get.

    In other words, I need to move from any node with an <id>XXX_X</i> to the next preceding-sibling cell with a control of xsi:type='Label' and a name node. From the node <id>MyItemId_1</> I need to get the second preceding-sibling, from the node <id>MyItemId_4</id> I need to get the 5th preceding-sibling.

    This is a sample xml piece:

    <cell>
        <control xsi:type="Label">
            <id>1234</id>
            <name>MyOtherItemName</name>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Label">
            <id>MyOtherItemId_0</id>
            <name/>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Label">
            <id>MyOtherItemId_1</id>
            <name/>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyOtherItemId_2</id>
            <name>552</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyOtherItemId_3</id>
            <name>432</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyOtherItemId_4</id>
            <name>33</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Label">
            <id>1234</id>
            <name>MyItemName</name>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Label">
            <id>MyItemId_0</id>
            <name/>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Label">
            <id>MyItemId_1</id>
            <name/>
            <message/>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyItemId_2</id>
            <name>552</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyItemId_3</id>
            <name>432</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    <cell>
        <control xsi:type="Button">
            <id>MyItemId_4</id>
            <name>33</name>
            <message/>
            <type>Link</type>
            <selected>false</selected>
        </control>
        <selected>false</selected>
        <style>Odd</style>
    </cell>
    

    I do get the item i have to get with this xpath:

    //cell[control[type='Link']]/control[type='Link' and selected='false' and id='MyItemId_3']/id
    

    That selects the id of the control of the cell, namely the 4th column in the second row, of the rendered table.

    From that node on I try moving to the first cell in the row by following this path:

    ../../preceding-sibling::cell[control[@xsi:type='Label' and name[node()]]]/control[name[node()]]/name
    

    That gives me the two correct cells of the first column of the table.

    <name>MyOtherItemName</name>
    * * * * * * * * * *
    <name>MyItemName</name>
    

    Now it breaks my back since I can't get it to just give me back the last one of the two selected.

    I tried this:

    ../../preceding-sibling::cell[control[@xsi:type='Label' and name[node()]]][1]/control[name[node()]]/name
    

    which is a preceding-sibling selection with a predicate to exactly the sort of siblings I search for, but it seems I can not combine that predicate with a [1] selector. Instead of selecting the desired first preceding sibling "MyItemName" it selects the first sibling from all preceding ones "MyOtherItemName".

    I need help, hope someone here has a clue and can pinpoint me in the right direction.

    Exactly what I set up to get this work is copying the xml into http://www.bit-101.com/xpath/ and working with the concatenated xpathes on it to simulate what the software should do:

    //cell[control[type='Link']]/control[type='Link' and selected='false' and id='MyItemId_3']/id/../../preceding-sibling::cell[control[@xsi:type='Label' and name[node()]]]/control[name[node()]]/name
    
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    that is right, but how do I use the selection of the first sibling only from those preceding siblings with that match the predicate? using preceding-sibling[PREDICATE][1] does not work - returns no node
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    this does not work if I try it with bit-101.com/xpath. simply nothing gets selected. The first part, before the preceding-sibling works in this example.
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    I need to test for the name node to exist and have text.
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    please try it out with bit-101/xpath, it won't work, if you mix predicate with selector.
  • forty-two
    forty-two over 12 years
    Just tried it in bit-101. It works for me and selects cell with name "MyOtherItemName"
  • choroba
    choroba over 12 years
    It does not work with bit-101, but it works with xsh: xsh.sourceforge.net
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    sorry, but i choose bit-101 because it behaved similar to the xml-engine i have to use - the one in SilkPerformer. Maybe xsh uses xpath2?
  • Oliver Friedrich
    Oliver Friedrich over 12 years
    yes, but it should show "MyItemName" because "MyItemId_4" is selected before.
  • forty-two
    forty-two over 12 years
    Hmm, it selects "MyItemName" when I try it in eclipse, but "MyOtherItemName" in bit-101. I'd say bit-101 is in error.
  • choroba
    choroba about 7 years
    @OliverFriedrich: It doesn't (I must know, I maintain it).