53

ancestor::foo[bar[@attr="val"]]

I thought this would work, but it's not. I need to find the nearest foo element going up in the tree that has a bar child element, which in turn has an attr attribute of val.

4
  • Nothing. It's not matching the element I described. Should that expression work?
    – Core Xii
    Commented Jun 9, 2010 at 12:33
  • 11
    Please, provide the (minimal) XML document on which this "doesn't work". We only know that you are "putting your hand in the bag and not finding what you want there" but we don't know "what is in the bag" :) Commented Jun 9, 2010 at 13:27
  • While a fair request, I'm 100% positive it should work on my XML. Mostly I'm just looking for someone to point out an obvious flaw in my expression. It's surprisingly difficult to find rich examples of XPath anywhere, especially dealing with moving UP in the tree rather than down, so I'm simply wondering if I'm doing something wrong here.
    – Core Xii
    Commented Jun 10, 2010 at 3:06
  • 1
    @CoreXii all you have to do is just give an example of xml, and what you expect, it will be much easier for us to take a look at
    – Luk Aron
    Commented Nov 26, 2019 at 16:47

4 Answers 4

58

This should work:

//*[ancestor::foo[bar[@attr="val"]]]

or alternatively

root/foo/bar[ancestor::foo[bar[@attr="val"]]]

matches the second <bar> element

<root>
    <foo>
        <bar attr="xxx"></bar>
    </foo>
    <foo>
        <bar attr="val"></bar>
    </foo>
    <foo>
        <bar attr="zzz"></bar>
    </foo>
</root>
5
  • 13
    Does this actually answer the question? The OP wants to select the ancestor (the foo).. this example selects the descendant (the bar) whose ancestor matches some criteria. Commented Jul 28, 2016 at 20:20
  • 11
    This doesn't answer the question. Not sure why it has been up-voted.
    – geoidesic
    Commented Aug 19, 2016 at 10:37
  • 1
    @geoidesic because it apparently solved the OPs problem
    – Gordon
    Commented Aug 19, 2016 at 10:38
  • 1
    But not the question :)
    – Nebulosar
    Commented Aug 23, 2021 at 7:06
  • Just because this is the best answer chosen by OP doesn't mean it's correct
    – Kyle
    Commented Mar 22, 2022 at 17:07
30

Updated to reflect a solution intended by OP.

See @David's answer

For a sample xml below,

<root>
<foo id="0">
    <bar attr="val"/>
    <foo id="1">
        <bar attr="xxx"/>
    </foo>
    <foo id="2">
        <bar attr="val">
            <one>1</one>
            <two>
                <three>3</three>
            </two>
        </bar>
    </foo>
</foo>

a valid xpath on the reverse axis with <three> as the context node, a valid solution is

./ancestor::foo[bar[@attr='val']][position() = 1]

"./" is optional. position() = 1 is not optional as otherwise the ancestor foo satisfying the predicates would also be returned.

Old answer:ignore

This is an old question. but anybody who is interested, the following should get you what the original question intended.

Reverse axis

//bar[@attr='val']/ancestor::*[position()=1]

Forward axis

//*[child::bar[@attr='val']]
7
  • 1
    The reverse of "ancestor" is "descendant", not "child". "child" is the reverse of "parent" instead of "ancestor"
    – wybe
    Commented Jun 26, 2017 at 10:20
  • @wybe I think you are confusing xpath axes directions with antonyms of an axis. Any axis which finds nodes in document order after the context node is a forward axis. An axis that finds nodes in document order before context node is a reverse axis. If that does not address what you intended, can you please provide what you really meant by that .
    – santon
    Commented Jun 26, 2017 at 22:48
  • @wybe Refer to w3.org/TR/xpath/#axes and under section 2.4 Predicates
    – santon
    Commented Jun 26, 2017 at 23:16
  • I meant "the opposite of ancestor is descendant" as in "the opposite of a parent's parent is a child's child". And similarly the other direction of "direct parent" is "direct child". Thanks for clearing this up
    – wybe
    Commented Jun 27, 2017 at 8:32
  • @wybe I agree with your statements but was confused about the context for that statement. child, descendent, following-sibling, following, descendent-or-self are all forward axis.
    – santon
    Commented Jun 27, 2017 at 21:21
6

In my opinion the best answer was provided by the person who posed the question. His solution was supposed to return all the ancestor foos. He wants only the nearest one which is the one with position()=1, so his xpath expression needs to be slightly amended to:

ancestor::foo[bar[@attr="val"] and position() = 1]

If he writes that the ancestor::foo[bar[@attr="val"]] didn't return anything, so he had some other problem in his xml or in his assumption about the current element of his XPath evaluation context.

The other answers (starting with //) do not really answer the question and even if they by chance met the need of someone, so they are not efficient: they choose ALL elements in the xml file, applying filter on them afterwards. While what the relative xpath proposed by me or the person who was asking this question is just going to search through few elements starting from the current node up to the parent and it's parent etc. - it will be very efficient even with large XML files.

1
  • I agree with your assessment. My answer addressed the problem from top down and does not address it from the context node. I will update my answer to reflect that. thx
    – santon
    Commented Jun 26, 2017 at 22:54
2

If you have a file like this:

<root>                                                                                                             
<foo id="0">                                                                                                       
    <foo id="1">                                                                                                   
        <bar attr="xxx" />                                                                                         
    </foo>                                                                                                         
    <foo id="2">                                                                                                   
        <bar attr="val" />                                                                                         
    </foo>                                                                                                         
    <foo id="3">                                                                                                   
        <tar>                                                                                                      
            <bar attr="val" />                                                                                     
        </tar>                                                                                                     
    </foo>                                                                                                         
</foo>                                                                                                             
</root>

and you want the foo nodes with the ids 2 and 3, i.e. the closest foos to their bar descendants having attr=val, the following works:

//foo[descendant::bar[@attr="val"] and not(descendant::foo)]

If you omit and not(descendant::foo) you get additionally the foo with the id 0.

The other answers didn't work for me on the general case of my example.

You can test it in Python with:

from lxml import etree
tree = etree.parse('example.xml')
foos = tree.xpath('//foo[descendant::bar[@attr="val"] and not(descendant::foo)]')
for foo in foos:
    print(foo.attrib)

which prints:

{'id': '2'}
{'id': '3'}

Notice that the xpath used is not efficient. I would love to learn a more efficient one.

Not the answer you're looking for? Browse other questions tagged or ask your own question.