XPath to select multiple tags

XmlXpath

Xml Problem Overview


Given this simplified data format:

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

How would you select all the Cs, Ds and Es that are children of B elements?

Basically, something like:

a/b/(c|d|e)

In my own situation, instead of just a/b/, the query leading up to selecting those C, D, E nodes is actually quite complex so I'd like to avoid doing this:

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

Is this possible?

Xml Solutions


Solution 1 - Xml

One correct answer is:

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

Do note that this

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

is both too-long and incorrect. This XPath expression will select nodes like:

OhMy:c

NotWanted:d 

QuiteDifferent:e

Solution 2 - Xml

You can avoid the repetition with an attribute test instead:

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

Contrary to Dimitre's antagonistic opinion, the above is not incorrect in a vacuum where the OP has not specified the interaction with namespaces. The self:: axis is namespace restrictive, local-name() is not. If the OP's intention is to capture c|d|e regardless of namespace (which I'd suggest is even a likely scenario given the OR nature of the problem) then it is "another answer that still has some positive votes" which is incorrect.

You can't be definitive without definition, though I'm quite happy to delete my answer as genuinely incorrect if the OP clarifies his question such that I am incorrect.

Solution 3 - Xml

Why not a/b/(c|d|e)? I just tried with Saxon XML library (wrapped up nicely with some Clojure goodness), and it seems to work. abc.xml is the doc described by 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>>)

Solution 4 - Xml

Not sure if this helps, but with XSL, I'd do something like:

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

and won't this XPath select all children of B nodes:

a/b/*

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionnickfView Question on Stackoverflow
Solution 1 - XmlDimitre NovatchevView Answer on Stackoverflow
Solution 2 - XmlannakataView Answer on Stackoverflow
Solution 3 - XmlPavel RepinView Answer on Stackoverflow
Solution 4 - XmlCalvinView Answer on Stackoverflow