How can I grab a parent node based on multiple child node values?
<parents>
<parent>
<item>
<name>200</name>
</item>
<item>
<name>201</name>
&开发者_运维问答lt;/item>
<item>
<name>204</name>
</item>
</parent>
<parent>
<item>
<name>203</name>
</item>
</parent>
</parents>
I need the first parent
node because I have a list of IDs that correspond to items. Consider this:
list = ['200', '201'];
Because 200
is found in the first parent
node, I want that parent
node. If 200
didn't exist, I would still get the parent node because 201
is a value of the /name/text()
residing in the first parent
.
My only requirement is that I need to grab the parent
node if even one
of the IDs in my list
is found.
Currently I'm only testing for the first, like so:
//name/child::text()[.="' . $firstKey . '"]/../../../../
Where $firstKey
is a reference to the first element in the list, 200
. This isn't ideal because it doesn't check for all values, but only the first one.
I. XPath 1.0 solution using XSLT 1.0 as host:
Use:
/*/parent[item/name = $vList]
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:list>
<val>200</val>
<val>201</val>
</my:list>
<xsl:variable name="vList" select=
"document('')/*/my:list/*"/>
<xsl:template match="/">
<xsl:copy-of select="*/parent[item/name = $vList]"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<parents>
<parent>
<item>
<name>200</name>
</item>
<item>
<name>201</name>
</item>
<item>
<name>204</name>
</item>
</parent>
<parent>
<item>
<name>203</name>
</item>
</parent>
</parents>
produces the wanted, correct result:
<parent>
<item>
<name>200</name>
</item>
<item>
<name>201</name>
</item>
<item>
<name>204</name>
</item>
</parent>
Note: You can pass what is now <my:list>
as a parameter to the transformation.
II. Using XPath 2.0:
/*/parent[item/name = ('200', '201')]
The XSLT 2.0 - based verification is below"
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:copy-of select="*/parent[item/name = ('200', '201')]"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the provided XML document (above), the same correct result is produced.
Something like this could be of help:
//parent[.//item[name[text()='200' or text()='201']]]
You'll have to construct the innermost predicate (with the text()='200' or ...
) programmatically, though. I'm not certain within which context you are using this, but since you're using a variable reference I'm assuming XSLT. I'll see if there's something more suitable...
EDIT: The only thing I can think of is concatenating all your keys with some symbol that you're sure they'll never contain as separator. For example:
200#201#...
Then use the XPath contains
function:
//parent[.//item[name[contains('200#201#...', normalize-space(text()))]]]
The first argument of contains would then have to be your concatenated keys, perhaps passed in as a variable. Function string-join
would be good for this, but I believe it's only available in XPath 2.0.
If you have XSLT 2.0, you can say
<xsl:variable name="list" select="(200, 201)" />
<xsl:variable name="theParent" select="(//parent[item/name = $list])[1]" />
The =
works as an existential comparison, returning true if any item in the nodeset on the left side is equal to any item in the sequence on the right side of the =
.
(Untested.)
精彩评论