开发者

How can I grab a parent node based on multiple child node values?

开发者 https://www.devze.com 2023-03-31 14:56 出处:网络
<parents> <parent> <item> <name>200</name> </item> <item> <name>201</name>
<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.)

0

精彩评论

暂无评论...
验证码 换一张
取 消