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 s и Es, которые являются детьми B элементов?

в основном, что-то вроде:

a/b/(c|d|e)

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

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

это возможно?

4 95

4 ответа:

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

/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

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

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

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

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

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

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