XPath для поиска ближайшего предка, который содержит элемент с атрибутом с определенным значением - PullRequest
22 голосов
/ 09 июня 2010

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

Я думал, что это будет работать, но это не так.Мне нужно найти ближайший элемент foo, идущий вверх по дереву, который имеет дочерний элемент bar, который в свою очередь имеет атрибут attr, равный val.

Ответы [ 4 ]

33 голосов
/ 08 октября 2010

Это должно работать:

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

или альтернативно

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

соответствует второму <bar> элементу

<root>
    <foo>
        <bar attr="xxx"></bar>
    </foo>
    <foo>
        <bar attr="val"></bar>
    </foo>
    <foo>
        <bar attr="zzz"></bar>
    </foo>
</root>
12 голосов
/ 30 октября 2016

Обновлено, чтобы отразить решение, предназначенное OP.

См. Ответ Дэвида

Для образца xml ниже,

<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>

допустимый xpath на обратной оси с в качестве узла контекста, допустимое решение -

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

"./" необязательно. position () = 1 не является обязательной, так как в противном случае также будет возвращен предок foo, удовлетворяющий предикатам.

Старый ответ: игнорировать

Это старый вопрос. но всем, кто заинтересован, следующее должно дать вам то, что предполагалось в первоначальном вопросе.

Обратная ось

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

Передняя ось

//*[child::bar[@attr='val']]
1 голос
/ 27 марта 2017

На мой взгляд, лучший ответ дал человек, который задал вопрос. Его решение должно было вернуть всех предков Фоос. Он хочет только ближайший, который с position () = 1, поэтому его выражение xpath должно быть слегка изменено так:

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

Если он пишет, что ancestor::foo[bar[@attr="val"]] ничего не возвращал, значит, у него была другая проблема в его xml или в его предположении о текущем элементе его контекста оценки XPath.

Другие ответы (начинающиеся с //) на самом деле не отвечают на вопрос, и даже если они случайно удовлетворяют потребности кого-то, поэтому они неэффективны: они выбирают ВСЕ элементы в XML-файле, применяя к ним фильтр впоследствии. Хотя то, что относительный xpath, предложенный мной или человеком, который задавал этот вопрос, просто будет искать в нескольких элементах, начиная с текущего узла и заканчивая родительским, его родительским и т. Д., - это будет очень эффективно даже с большими файлами XML.

0 голосов
/ 28 сентября 2018

Если у вас есть такой файл:

<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>

и вам нужны узлы foo с идентификаторами 2 и 3, т.е. самые близкие foos к их bar потомкам, имеющим attr=val, следующие работы:

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

Если вы опустите and not(descendant::foo), вы получите дополнительно foo с идентификатором 0.

Другие ответы не помогли мне в общем случае моего примера.

Вы можете проверить это на Python с помощью:

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)

который печатает:

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

Обратите внимание, что используемый xpath неэффективен. Я хотел бы изучить более эффективный.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...