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

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

<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

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

Ответ 1

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

/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

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

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

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

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

Ответ 3

Почему бы не 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>>)

Ответ 4

Не уверен, что это поможет, но с 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/*