196

I need get the parent node for child node title 50

At the moment I am using only

//*[title="50"]

How could I get its parent? Result should be the store node.


<?xml version="1.0" encoding="utf-8"?>
<d:data xmlns:d="defiant-namespace" d:mi="23">
    <store d:mi="22">
        <book price="12.99" d:price="Number" d:mi="4">
            <title d:constr="String" d:mi="1">Sword of Honour</title>
            <category d:constr="String" d:mi="2">fiction</category>
            <author d:constr="String" d:mi="3">Evelyn Waugh</author>
        </book>
        <book price="8.99" d:price="Number" d:mi="9">
            <title d:constr="String" d:mi="5">Moby Dick</title>
            <category d:constr="String" d:mi="6">fiction</category>
            <author d:constr="String" d:mi="7">Herman Melville</author>
            <isbn d:constr="String" d:mi="8">0-553-21311-3</isbn>
        </book>
        <book price="8.95" d:price="Number" d:mi="13">
            <title d:constr="String" d:mi="10">50</title>
            <category d:constr="String" d:mi="11">reference</category>
            <author d:constr="String" d:mi="12">Nigel Rees</author>
        </book>
        <book price="22.99" d:price="Number" d:mi="18">
            <title d:constr="String" d:mi="14">The Lord of the Rings</title>
            <category d:constr="String" d:mi="15">fiction</category>
            <author d:constr="String" d:mi="16">J. R. R. Tolkien</author>
            <isbn d:constr="String" d:mi="17">0-395-19395-8</isbn>
        </book>
        <bicycle price="19.95" d:price="Number" d:mi="21">
            <brand d:constr="String" d:mi="19">Cannondale</brand>
            <color d:constr="String" d:mi="20">red</color>
        </bicycle>
    </store>
</d:data>
iliketocode
  • 6,652
  • 4
  • 41
  • 57
GibboK
  • 64,078
  • 128
  • 380
  • 620

5 Answers5

355

Use the parent axes with the parent node's name.

//*[title="50"]/parent::store

This XPath will only select the parent node if it is a store.

But you can also use one of these

//*[title="50"]/parent::*
//*[title="50"]/..

These xpaths will select any parent node. So if the document changes you will always select a node, even if it is not the node you expect.

EDIT

What happens in the given example where the parent is a bicycle but the parent of the parent is a store?

Does it ascent?

No, it only selects the store if it is a parent of the node that matches //*[title="50"].

If not, is there a method to ascent in such cases and return None if there is no such parent?

Yes, you can use ancestor axes

//*[title="50"]/ancestor::store

This will select all ancestors of the node matching //*[title="50"] that are ` stores. E.g.

<data xmlns:d="defiant-namespace" d:mi="23">
    <store mi="1">
        <store mi="22">
            <book price="8.95" d:price="Number" d:mi="13">
                <title d:constr="String" d:mi="10">50</title>
                <category d:constr="String" d:mi="11">reference</category>
                <author d:constr="String" d:mi="12">Nigel Rees</author>
            </book>
        </store>
    </store>
</data>

XPath selection result

René Link
  • 38,761
  • 11
  • 84
  • 115
  • 7
    You could also use the `parent::*` abbreviated syntax `..` (example: `//*[title="50"]/..`) or a nested predicate (example: `//*[*[title="50"]]`) – Daniel Haley Jan 30 '15 at 19:17
  • 1
    Additional info: http://www.w3.org/TR/xpath/#axes and http://www.w3.org/TR/xpath/#path-abbrev – Daniel Haley Jan 30 '15 at 19:36
  • 3
    Or `parent::node()` which corresponds to the shortcut `..` and is better in my opinion. In fact `parent::*` is limited to the principal node type of the axis which in most cases is not a problem. – Ludovic Kuty Dec 03 '15 at 16:30
  • 3
    If you are using try `.xpath('..')` or `xpath('parent::node()')` – briankip Apr 07 '16 at 17:35
  • 1
    "This XPath will only select the parent node if it is a store." - and what happens in the given example where the parent is `bicycle` but the parent of the parent is `store`? Does it ascent? If not, is there a method to ascent in such cases and return `None` if there is no such parent? – Martin Thoma Aug 17 '17 at 14:13
  • what a well written super helpful answer: i can directly use `ancestor::` to solve my task – StephenBoesch Dec 19 '19 at 22:18
  • For the life of me I couldn't select the Store element if it is the root element unless I put it inside another container. I'd have thought /[field='value'] would get it (if i put inside a container root then /Store[field='value'] works. iow valuevalue then works with /Store[field='value']. Here is what finally worked for me with Store as root: /*/..[field='value'] – Skychan Sep 09 '20 at 21:54
37

Just as an alternative, you can use ancestor.

//*[title="50"]/ancestor::store

It's more powerful than parent since it can get even the grandparent or great great grandparent

Aminah Nuraini
  • 13,849
  • 6
  • 73
  • 92
13

You can use the two dots at the end of expression, too. See this example:

//*[title="50"]/..
phduarte
  • 305
  • 2
  • 9
7

New, improved answer to an old, frequently asked question...

How could I get its parent? Result should be the store node.

Use a predicate rather than the parent:: or ancestor:: axis

Most answers here select the title and then traverse up to the targeted parent or ancestor (store) element. A simpler, direct approach is to select parent or ancestor element directly in the first place, obviating the need to traverse to a parent:: or ancestor:: axes:

//*[book/title = "50"]

Should the intervening elements vary in name:

//*[*/title = "50"]

Or, in name and depth:

//*[.//title = "50"]
kjhughes
  • 89,675
  • 16
  • 141
  • 199
  • 1
    This doesn't answer the question. The author doesn't necessarily know the parent name ahead of time. – Kenn Sebesta Feb 14 '20 at 19:43
  • 1
    @KennSebesta: I've updated the answer to address your concern. Please remove your downvote if your objection has been addressed, or let me know if you feel something remains wrong with the answer. Thanks. – kjhughes Feb 15 '20 at 02:57
0

This works in my case. I hope you can extract meaning out of it.

//div[text()='building1' and @class='wrap']/ancestor::tr/td/div/div[@class='x-grid-row-checker']
Dan Atkinson
  • 10,801
  • 12
  • 78
  • 106
Shivam Bharadwaj
  • 1,130
  • 14
  • 17