tags:

views:

313

answers:

4

Let's say I have the following XML file

<a id="123">
   <b type="foo" value="1" />
   <b type="baz" value="1" />
</a>
<a id="789">
  <b type="bar" value="12" />
</a>
<a id="999">
   <b type="foo", value="2" />
</a>

I want to get a list of all the 'a' nodes that have a 'b' subnode with a type="foo" and value="1". You can do something similar in jQuery with the ":has" selector.

For the record I'm planning on using xmlstarlet on the command line (but I'm not married to doing it that way), so a xslt that works that way would be best.

+4  A: 

something like this:

a[b[@type='foo'][@value='1']]

should do the trick

gizmo
I didn't know you could do node[node[something]], that's an elegant and clean way to do it. :)
Rory
A: 
<xsl:variable name="nodeList" select="a[b[@type='foo' and @value=1]]"/>

<xsl:for-each select="$nodeList">
    <xsl:value-of select="."/>
</xsl:for-each>
Nick Allen - Tungle139
You've got the /b[@type='foo' and @value=1]] from Maurice Perry, but it's more complicated and verbose than I was looking for.
Rory
Fair enough if it's more complicated than you want but I did NOT copy it from another SO user
Nick Allen - Tungle139
The OP wants a "list of 'a' nodes"" -- not a list of the string value of the "a" nodes.
Dimitre Novatchev
Oh I'm not accusing you of copying. I'm saying that you and Maurice Perry have the same insight, but yours is slightly more complicated.
Rory
A: 

I think this would be: /b[@type='foo' and @value=1]/parent::a

Maurice Perry
This is not the answer. This XPath expression selects the *top* node and only if its name is "b". The top node is a single node and it never has a parent that is also an element.
Dimitre Novatchev
+1  A: 

This can be done using a single XPath expression as pointed out in gizmo's answer.

Because the question is specifically for XSLT, here is an efficient XSLT solution using keys:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
 <xsl:output omit-xml-declaration="yes"/>
<!--                                      --> 
 <xsl:key name="kAByBTypeVal" 
  match="a"
  use="concat(b/@type,'+',b/@value)"/>
<!--                                      -->       
    <xsl:template match="/">
      <xsl:copy-of select=
       "key('kAByBTypeVal', 'bar+12')"/>
    </xsl:template>
</xsl:stylesheet>

When the above transformation is applied on this XML document:

<t>
    <a id="123">
     <b type="foo" value="1" />
     <b type="baz" value="1" />
    </a>
    <a id="789">
     <b type="bar" value="12" />
    </a>
    <a id="999">
     <b type="foo" value="2" />
    </a>
</t>

the correct result is produced:

<a id="789">
  <b type="bar" value="12"/>
</a>
Dimitre Novatchev