XSLT - 选择另一个节点之后的节点

时间:2011-06-13 19:57:32

标签: xml xslt select xquery

我正在尝试选择1)来自具有特定属性的节点的所有节点,并且2)自己具有特定属性。所以,如果我有以下XML:

<node id="1"><child attr="valueOfInterest"/></node>
<node id="2"><child attr="boringValue"/></node>
...
<node id="3"><child attr="valueOfInterest"/></node>
<node id="4"><child attr="boringValue"/></node>
<node id="5"><child attr="boringValue"/></node>
<node id="6"><child attr="boringValue"/></node>
...

我的XSLT遍历每个node标记。在每个node,我希望它选择自node node child attr以来valueOfInterest以来发生的所有先前<xsl:variable name="prev_vals" select="preceding-sibling::node/child[@attr = $someValueICareAbout]/@attr"/> attr }}。所以,如果我在节点#2,我想要一个空节点集。如果我在节点#6,我想选择节点#4和5.我目前有以下XSLT:

attr

因此,此XSLT获取特定值的所有前面的node值。我如何仅获取node之后child之前的attr值,id具有node具有特定<xsl:variable name="prev_children_of_interest" select="preceding-sibling::node/child[@attr != $someValueICareAbout]"/> <xsl:variable name="mru_child_of_interest" select="$prev_children_of_interest[count($prev_children_of_interest)]"/> 值的最新值child ,“valueOfInterest”)? attr=valueOfInterest标记上的child属性不保证会增加,因此我们无法与之进行比较。

编辑:我认为这些可能有用:

mru_child_of_interest

这是所有以前的parent标记{{1}},然后是最近使用的(最接近当前节点){{1}}标记,其中包含我正在寻找的属性。从{{1}}我们可以找到最近使用的{{1}}标记,但是我们如何查找该标记之后的节点?

4 个答案:

答案 0 :(得分:6)

我不确定我是否正确理解了您的问题,但这里有一些XSL 1.0(其他each-nodes属性仅供参考):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="nodes">
        <xsl:copy>
            <xsl:apply-templates select="node"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="node">
        <xsl:variable name="someValueICareAbout">valueOfInterest</xsl:variable>

        <xsl:variable name="recentParticularNode"
            select="preceding-sibling::node[child/@attr = $someValueICareAbout][1]"/>

        <xsl:variable name="recentParticularNodePosition"
            select="count($recentParticularNode/preceding-sibling::node) + 1"/>

        <xsl:variable name="currentPosition" select="position()"/>

        <xsl:if test="child/@attr != $someValueICareAbout">
            <each-nodes id="{@id}" cp="{$currentPosition}" 
                    rpnp="{$recentParticularNodePosition}">
                <xsl:copy-of
                    select="../node[position() &gt; $recentParticularNodePosition
                    and position() &lt; $currentPosition]"/>
            </each-nodes>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

输入XML:

<?xml version="1.0" encoding="UTF-8"?>
<nodes>
    <node id="1"><child attr="valueOfInterest"/></node>
    <node id="2"><child attr="boringValue2"/></node>
    <node id="3"><child attr="valueOfInterest"/></node>
    <node id="4"><child attr="boringValue4"/></node>
    <node id="5"><child attr="boringValue5"/></node>
    <node id="6"><child attr="boringValue6"/></node>
</nodes>

结果XML:

<?xml version="1.0" encoding="UTF-8"?>
<nodes>
   <each-nodes id="2" cp="2" rpnp="1"/>
   <each-nodes id="4" cp="4" rpnp="3"/>
   <each-nodes id="5" cp="5" rpnp="3">
      <node id="4">
         <child attr="boringValue4"/>
      </node>
   </each-nodes>
   <each-nodes id="6" cp="6" rpnp="3">
      <node id="4">
         <child attr="boringValue4"/>
      </node>
      <node id="5">
         <child attr="boringValue5"/>
      </node>
   </each-nodes>
</nodes>

答案 1 :(得分:3)

看起来你想要两组的交集。集合1是最后一个valueOfInterest之后的所有节点。 Set 2是当前节点之前不包含valueOfInterest的所有节点。对于XPath 1.0,以下内容将为您提供交集(找到引用here)。

$set1[count($set2|.)=count($set2)]

根据您的输入,以下XSL演示了您要查找的节点集

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:strip-space elements="*"/>
  <xsl:variable name="someValueICareAbout">valueOfInterest</xsl:variable>
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="node[child/@attr='boringValue']">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>

      <xsl:variable name="set1" select="preceding-sibling::node[child/@attr='valueOfInterest'][1]/following-sibling::node "/>

      <xsl:variable name="set2" select="preceding-sibling::node[child/@attr='boringValue']"/>

      <predecessors>
          <xsl:copy-of select="$set1[count($set2|.)=count($set2)]"/>
      </predecessors>

    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

这是输出

<xml>
  <node id="1">
    <child attr="valueOfInterest"/>
  </node>
  <node id="2">
    <child attr="boringValue"/>
    <predecessors/>
  </node>
  <node id="3">
    <child attr="valueOfInterest"/>
  </node>
  <node id="4">
    <child attr="boringValue"/>
    <predecessors/>
  </node>
  <node id="5">
    <child attr="boringValue"/>
    <predecessors>
      <node id="4">
        <child attr="boringValue"/>
      </node>
    </predecessors>
  </node>
  <node id="6">
    <child attr="boringValue"/>
    <predecessors>
      <node id="4">
        <child attr="boringValue"/>
      </node>
      <node id="5">
        <child attr="boringValue"/>
      </node>
    </predecessors>
  </node>
</xml>

请注意,我在[1]中使用preceding-sibling::node[child/@attr='valueOfInterest'][1]的原因是因为preceding-sibling see here推翻了节点集的顺序。

如果您有XPath 2.0,则可以使用intersect运算符

  <predecessors>
    <xsl:copy-of select="$set1 intersect $set2"/>
  </predecessors>

这会产生相同的结果。

答案 2 :(得分:3)

此转换完全复制了所需的节点

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match=
 "node[not(child/attr='valueOfInterest')]">

 <xsl:variable name="vFollowing" select=
 "preceding-sibling::node
          [child/@attr='valueOfInterest'][1]
            /following-sibling::node"/>

 <xsl:variable name="vPreceding" select=
  "preceding-sibling::node"/>

  <xsl:copy-of select=
  "$vFollowing[count(. | $vPreceding)
              =
               count($vPreceding)
              ]
  "/>
======================
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

应用于此XML文档(基于提供的XML片段并将其包装在顶部元素中以使其成为格式良好的XML文档):

<t>
    <node id="1">
        <child attr="valueOfInterest"/>
    </node>
    <node id="2">
        <child attr="boringValue"/>
    </node>...
    <node id="3">
        <child attr="valueOfInterest"/>
    </node>
    <node id="4">
        <child attr="boringValue"/>
    </node>
    <node id="5">
        <child attr="boringValue"/>
    </node>
    <node id="6">
        <child attr="boringValue"/>
    </node>...
</t>

产生了想要的正确结果

======================

======================
 <node id="2">
   <child attr="boringValue"/>
</node>
======================

======================
 <node id="4">
   <child attr="boringValue"/>
</node>
======================
 <node id="4">
   <child attr="boringValue"/>
</node>
<node id="5">
   <child attr="boringValue"/>
</node>
======================

<强>解释

  1. 这里我们使用众所周知的 Kayessian 公式(由SO用户@Michael Kay发现),用于两个节点集$ns1和{{1}的交集}:

    ns1 [count(。| $ ns2)= count($ ns2)]

  2. 我们只需将$ns2$vFollowing替换为上述公式中的$vPreceding$ns1

  3. 满足条件的

    $ns2节点$vFollowing is defined to contain exactly all the following sibling elements named node`(有趣)。

    of the nearest是当前(匹配)节点的兄弟之前的所有$vPreceding元素的集合。

    0.3。它们的交集正好是所需的节点集。

答案 3 :(得分:2)

这是在XSLT 2.0中实现它的一种方法:

<xsl:variable name="prevVOI"
      select="(preceding-sibling::node[child/@attr = 'valueOfInterest'])[last()]" />
<xsl:variable name="prevNodesAfterVOI"
      select="preceding-sibling::node[. >> $prevVOI]" />