XPath: получить родительский узел от дочернего узла

157

Мне нужно получить родительский узел для дочернего узла title 50

На данный момент я использую только

//*[title="50"]

Как я мог получить его родителя? Результатом должен быть storeузел.


<?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>
GibboK
источник

Ответы:

297

Используйте parentоси с именем родительского узла.

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

Этот XPath только выберет родительский узел, если он является store.

Но вы также можете использовать один из этих

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

Эти xpaths выберут любой родительский узел. Поэтому, если документ изменяется, вы всегда будете выбирать узел, даже если это не тот узел, который вы ожидаете.

РЕДАКТИРОВАТЬ

Что происходит в данном примере, где родитель - это велосипед, а родитель - это магазин?

Это восхождение?

Нет, он выбирает хранилище только в том случае, если он является родителем соответствующего узла //*[title="50"].

Если нет, есть ли способ подняться в таких случаях и вернуть None, если такого родителя нет?

Да, вы можете использовать ancestorоси

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

Это выберет всех предков соответствующего узла, //*[title="50"]которые являются `хранилищами. Например

<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

Рене Линк
источник
7
Вы можете также использовать parent::*сокращенный синтаксис ..(пример: //*[title="50"]/..) или вложенный предикат (пример: //*[*[title="50"]])
Daniel Хейли
1
Дополнительная информация: w3.org/TR/xpath/#axes and w3.org/TR/xpath/#path-abbrev
Даниэль Хейли
3
Или parent::node()что соответствует ярлыку ..и лучше на мой взгляд. Фактически parent::*ограничивается типом главного узла оси, что в большинстве случаев не является проблемой.
Людовик Куты
3
Если вы используете попробуйте .xpath('..')илиxpath('parent::node()')
briankip
«Этот XPath выберет родительский узел, только если он является хранилищем». - и что происходит в данном примере, где родитель является bicycleродителем, а родитель store? Это восхождение? Если нет, есть ли способ подняться в таких случаях и вернуть, Noneесли такого родителя нет?
Мартин Тома
31

Так же, как альтернативу, вы можете использовать ancestor.

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

Это более мощный, чем, parentпоскольку он может получить даже прародителя или прадеда

Амина Нураини
источник
20
Это не "более мощный". Это просто другое. Таким образом, выбор оси зависит от варианта использования.
Людовик Куты
11

Вы также можете использовать две точки в конце выражения. Смотрите этот пример:

//*[title="50"]/..
phduarte
источник
2

Новый, улучшенный ответ на старый, часто задаваемый вопрос ...

Как я мог получить его родителя? Результатом должен быть storeузел.

Используйте предикат, а не ось parent::илиancestor::

Большинство ответов здесь выбирают titleи затем переходят к целевому storeэлементу parent или ancestor ( ). Более простой, прямой подход состоит в том, чтобы сначала выбрать родительский элемент или элемент-предок непосредственно, избегая необходимости проходить по осям a parent::или ancestor:::

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

Если промежуточные элементы различаются по названию:

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

Или по названию и по глубине:

//*[.//title = "50"]
kjhughes
источник
Это не отвечает на вопрос. Автор не обязательно знает имя родителя заранее.
Кенн Себеста
1
@KennSebesta: я обновил ответ для решения вашей проблемы. Пожалуйста, удалите свой отрицательный голос, если ваше возражение было рассмотрено, или дайте мне знать, если вы чувствуете, что что-то не так с ответом Спасибо.
kjhughes
0

Это работает в моем случае. Я надеюсь, что вы можете извлечь смысл из этого.

//div[text()='building1' and @class='wrap']/ancestor::tr/td/div/div[@class='x-grid-row-checker']
Шивам Бхарадвадж
источник