use xpath to select elements with a set of multiple attributes/values

30

7

I have an XML document that i need to strip out particular pieces of data

the xml document has a structure as follows:-

<a>
   <b select='yes please'>
       <c d='text1' e='text11'/>
       <c d='text2' e='text12'/>
       <c d='text3' e='text13'/>
       <c d='text4' e='text14'/>
       <c d='text5' e='text15'/>
   </b>
 </a>
<a>
   <b select='no thanks'>
       <c d='text1' e='text21'/>
       <c d='text3' e='text23'/>
       <c d='text5' e='text25'/>
   </b>
 </a>
<a>
   <b select='yes please'>
       <c d='text1' e='text31'/>
       <c d='text2' e='text32'/>
       <c d='text3' e='text33'/>
       <c d='text4' e='text34'/>
       <c d='text5' e='text35'/>
   </b>
 </a>
<a>
   <b select='no thanks'>
       <c d='text4' e='text41'/>
       <c d='text3' e='text43'/>
       <c d='text5' e='text45'/>
   </b>
 </a>

i need to select only those /a/b element groups that have d attribute = 'text1' and d attribute = 'text4', once i have identified these sub documents i want to get the value of the e attributes with d attribute value 'text5'

hope thats clear

Cheers

DD

Hector

Posted 2011-07-16T15:26:15.677

Reputation: 1 761

1+1 for xpath/xslt sunday question :) – Emiliano Poggi – 2011-07-16T15:51:07.633

7ops, it's saturday... – Emiliano Poggi – 2011-07-16T15:52:12.300

@empo, In some zones is Sunday already :-) – Kirill Polishchuk – 2011-07-16T15:58:29.410

Good question, +1. See my answer for a single XPath expression that selects exactly the wanted attributes. – Dimitre Novatchev – 2011-07-16T17:21:49.937

Answers

34

You can use this XPath:

//a[b/c/@d = 'text1' and b/c/@d = 'text4']/b/c[@d = 'text5']/@e

It will select e='text15' and e='text35' of 1st and 3rd a/b

XSLT:

<xsl:template match="//a[b/c/@d = 'text1' and b/c/@d = 'text4']/b/c[@d = 'text5']">
  <xsl:value-of select="@e"/>
</xsl:template>

Kirill Polishchuk

Posted 2011-07-16T15:26:15.677

Reputation: 43 180

very nice, and thanks for your time and effort – Hector – 2011-07-16T16:24:51.527

@user423199, You're welcome! – Kirill Polishchuk – 2011-07-16T16:26:26.667

@user423199, You can mark as answer or upvote appropriate answer. – Kirill Polishchuk – 2011-07-16T16:29:39.943

@downvoter, Care to comment? – Kirill Polishchuk – 2011-07-16T17:29:18.637

1I'm not the downvoter. But I can think this as a subtle downvote, referring to the fact that you predicate on the ancestor a of c, while (as shown in other answers) it would be more natural predicate on the immediate parent b of c. You loose also readability. – Emiliano Poggi – 2011-07-16T19:27:10.020

@empo, mb you're right... :-( – Kirill Polishchuk – 2011-07-16T19:30:44.660

5@empo, But if it is so "subtle", @downvoter could explain his opinion. I don't like silent @downvoters. – Kirill Polishchuk – 2011-07-16T19:35:19.840

4

You can use a single template to match the required group, and then you get the value of the required attributes:

<xsl:template match="/*/a/b[c[@d='text1'] and c[@d='text4']]">
    <xsl:value-of select="c[@d='text5']/@e"/>
</xsl:template>

assuming:

<root>
    <a>
        <b select='yes please'>
            <c d='text1' e='text11'/>
            <c d='text2' e='text12'/>
            <c d='text3' e='text13'/>
            <c d='text4' e='text14'/>
            <c d='text5' e='text15'/>
        </b>
    </a>
    <a>
        <b select='no thanks'>
            <c d='text1' e='text21'/>
            <c d='text3' e='text23'/>
            <c d='text5' e='text25'/>
        </b>
    </a>
    <a>
        <b select='yes please'>
            <c d='text1' e='text31'/>
            <c d='text2' e='text32'/>
            <c d='text3' e='text33'/>
            <c d='text4' e='text34'/>
            <c d='text5' e='text35'/>
        </b>
    </a>
    <a>
        <b select='no thanks'>
            <c d='text4' e='text41'/>
            <c d='text3' e='text43'/>
            <c d='text5' e='text45'/>
        </b>
    </a>
</root>

the output will be text15 and text35.

Emiliano Poggi

Posted 2011-07-16T15:26:15.677

Reputation: 19 622

thanks for bothering to look at my problem, it was driving me mad! thankyou for spending time on it – Hector – 2011-07-16T16:29:58.473

@user you are welcome. The "tricky" part here is matching an element for which we have at least two instances of same children with different attribute value. You need to use and and, for clarity, a nested predicate. – Emiliano Poggi – 2011-07-16T17:12:56.613

4

i need to select only those /a/b element groups that have d attribute = 'text1' and d attribute = 'text4', once i have identified these sub documents i want to get the value of the e attributes with d attribute value 'text5'

hope thats clear

Yes, it's so clear the translation into XPath is almost mechanical

(: those /a/b element groups :) a/b 
(: that have d attribute = 'text1' :) [c/@d='text1'] 
(: and d attribute = 'text4' :) [c/@d='text4'] 
(: and .. i want to get the value of the e attributes 
   with d attribute value 'text5' :) / c[@d='text5'] / @e

Michael Kay

Posted 2011-07-16T15:26:15.677

Reputation: 107 624

1

FYI- For those not familiar, (: :) are XPath 2.0 comments. http://www.w3.org/TR/xpath20/#prod-xpath-Comment If you are using XPath 1.0, remove the comments and just use the XPath statement i.e. a/b[c/@d='text1'][c/@d='text4']/c[@d='text5']/@e

– Mads Hansen – 2011-07-16T17:22:37.010

1

Use this single XPath expression:

/*/a/b[c/@d='text1' and c/@d='text4']
         /c[@d='text5']
             /@e

Dimitre Novatchev

Posted 2011-07-16T15:26:15.677

Reputation: 209 189