XPath для выбора нескольких тегов

100

Учитывая этот упрощенный формат данных:

<a>
    <b>
        <c>C1</c>
        <d>D1</d>
        <e>E1</e>
        <f>don't select this one</f>
    </b>
    <b>
        <c>C2</c>
        <d>D2</d>
        <e>E1</e>
        <g>don't select me</g>
    </b>
    <c>not this one</c>
    <d>nor this one</d>
    <e>definitely not this one</e>
</a>

Как вы выберете все C s, D и E, которые являются дочерними элементами B?

В принципе, что-то вроде:

a/b/(c|d|e)

В моей собственной ситуации вместо простого a/b/ запрос, вызывающий выбор узлов C, D, E, на самом деле довольно сложный, поэтому я хотел бы избежать этого:

a/b/c|a/b/d|a/b/e

Возможно ли это?

Теги:
xpath

4 ответа

168
Лучший ответ

Один правильный ответ:

/a/b/*[self::c or self::d or self::e]

Обратите внимание, что это

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

является слишком длинным и неправильным. Это выражение XPath будет выбирать узлы, такие как:

OhMy:c

NotWanted:d 

QuiteDifferent:e
  • 2
    'or' не работает для каждого, вам нужно будет использовать вертикальную линию вместо '|'
  • 7
    @ Guasqueño, or является логическим оператором - он работает с двумя логическими значениями. Оператор объединения XPath | работает на двух наборах узлов. Они совершенно разные, и для каждого из них существуют конкретные варианты использования. Использование | может решить исходную проблему, но это приводит к более длинному и сложному пониманию выражения XPath. Более простое выражение в этом ответе, использующее оператор or создает требуемый набор узлов и может быть указано в атрибуте «select» операции XSLT <xsl:for-each> . Просто попробуйте.
Показать ещё 5 комментариев
43

Вместо этого вы можете избежать повторения с помощью теста атрибута:

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

В отличие от антагонистического мнения Димитрия, вышесказанное не является ошибочным в вакууме, где ОП не задает взаимодействия с пространствами имен. Ось self:: ограничивает пространство имен, local-name() - нет. Если намерение OP состоит в том, чтобы захватить c|d|e независимо от пространства имен (которое я бы предположил, это даже вероятный сценарий, учитывая характер OR проблемы), то это "еще один ответ, который все еще имеет положительные голоса", что неверно.

Вы не можете быть окончательными без определения, хотя я вполне счастлив удалить свой ответ как действительно неверный, если ОП разъяснит свой вопрос таким образом, что я ошибаюсь.

  • 3
    Выступая здесь как третье лицо - лично я считаю предложение Димитра лучшей практикой, за исключением случаев, когда у пользователя есть явная (и веская) причина заботиться о имени тега, не имеющем отношения к пространству имен; если бы кто-то сделал это с документом, который я смешивал в контенте с разным пространством имен (предположительно, предназначенным для чтения другим набором инструментов), я бы посчитал их поведение очень неуместным. Тем не менее, аргумент - как вы предлагаете - немного неприличным.
  • 3
    @annakata: спасибо за публикацию этого примера, потому что это именно то, что я искал, и у меня есть свои причины для этого.
Показать ещё 2 комментария
12

Почему бы не a/b/(c|d|e)? Я просто попробовал саксоновскую XML-библиотеку (хорошо обернулся с некоторой добротой Clojure), и, похоже, она работает. abc.xml - это документ, описанный OP.

(require '[saxon :as xml])
(def abc-doc (xml/compile-xml (slurp "abc.xml")))
(xml/query "a/b/(c|d|e)" abc-doc)
=> (#<XdmNode <c>C1</c>>
    #<XdmNode <d>D1</d>>
    #<XdmNode <e>E1</e>>
    #<XdmNode <c>C2</c>>
    #<XdmNode <d>D2</d>>
    #<XdmNode <e>E1</e>>)
  • 6
    Да, но это XPath 2.0
  • 1
    @ Алехандро, круто! Я был (блаженно) 4 года в мире XML, похоже, что XPath поднялся до 2.0 :)
Показать ещё 1 комментарий
-1

Не уверен, что это поможет, но с XSL я бы сделал что-то вроде:

<xsl:for-each select="a/b">
    <xsl:value-of select="c"/>
    <xsl:value-of select="d"/>
    <xsl:value-of select="e"/>
</xsl:for-each>

и не будет ли этот XPath выбирать всех дочерних узлов B-узлов:

a/b/*
  • 0
    Спасибо Кэлвину, но я не использую XSL, и под B на самом деле есть больше элементов, которые я не хочу выбирать. Я обновлю свой пример, чтобы быть более понятным.
  • 0
    О, хорошо в этом случае, кажется, у Аннакаты есть решение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню