265

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>
John Smith
  • 7,243
  • 6
  • 49
  • 61
GibboK
  • 71,848
  • 143
  • 435
  • 658

5 Answers5

462

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
  • 48,224
  • 13
  • 108
  • 140
  • 9
    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
  • 5
    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 – WestCoastProjects 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'] – Skystrider Sep 09 '20 at 21:54
50

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
  • 18,120
  • 8
  • 90
  • 108
22

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
  • 106,133
  • 27
  • 181
  • 240
19

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

//*[title="50"]/..
phduarte
  • 365
  • 2
  • 9
0
    //store[book[title[text()="50"]]]

or

    //store[*[title[text()="50"]]]
  • Why do you prefer this over the other options? And especially the accepted answer, which includes a lengthy explanation, and has been validated by 450 members of the community? Please [edit] your answer to help explain the benefits of this approach, and especially compared to previously established proposals. – Jeremy Caney May 07 '23 at 01:25