代码之家  ›  专栏  ›  技术社区  ›  philsquared

使用xpath查找节点集中节点的位置

  •  5
  • philsquared  · 技术社区  · 14 年前

    玩过之后 position() 我徒劳地到处寻找一个解决方案,结果 this older stackoverflow question 哪一个 几乎 描述我的问题。

    为了说明这一点,我将修改链接问题中的示例以符合我的要求。注意每个 <b> 元素在不同的 <a> 元素。这是关键的一点。

    <root>
        <a>
            <b>zyx</b>
        </a>
        <a>
            <b>wvu</b>
        </a>
        <a>
            <b>tsr</b>
        </a>
        <a>
            <b>qpo</b>
        </a>
    </root>
    

    a/b 我会得到四个人的点头 <b> 在那个节点集中 包含字符串的节点的 'tsr' . 另一篇文章中的解决方案如下: count(a/b[.='tsr']/preceding-sibling::*)+1 1 因为 preceding-sibling

    是否可以在上下文节点集内工作?

    6 回复  |  直到 7 年前
        1
  •  5
  •   Dimitre Novatchev    14 年前

    这是一个 在属于同一文档中任何节点集的任何节点上工作的解决方案 :

    我使用XSLT来实现这个解决方案,但是最终获得了一个可以与任何其他宿主语言一起使用的XPath表达式。

    $vNodeSet 作为节点集 $vNode 是此节点集中要查找其位置的节点。

    那么,让 $vPrecNodes 包含前面的XML文档中的所有节点 $V节点

    那么,让 $vAncNodes 包含XML文档中的所有祖先节点 $V节点

    $V节点 那是先例 在文档顺序中,节点集中的所有节点也属于 $vprec节点

    我将使用著名的Kaysian公式计算两个节点集的交集:

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

    $ns1 具有 $ns2 .

    基于这些,让 $vPrecInNodeSet 是中的节点集 $V节点 那是先例 按文件顺序排列。下面的XPath表达式定义 :

    $vNodeSet
          [count(.|$vPrecNodes) = count($vPrecNodes)
          or
           count(.|$vAncNodes) = count($vAncNodes)
          ]
    

    最后,想要的位置是 : count($vPrecInNodeSet) +1

    以下是这一切如何协同工作:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
     <xsl:variable name="vNodeSet" select="/*/a/b"/>
    
     <xsl:variable name="vNode" select="$vNodeSet[. = 'tsr'][1]"/>
    
     <xsl:variable name="vPrecNodes" select="$vNode/preceding::node()"/>
    
     <xsl:variable name="vAncNodes" select="$vNode/ancestor::node()"/>
    
     <xsl:variable name="vPrecInNodeSet" select=
      "$vNodeSet
          [count(.|$vPrecNodes) = count($vPrecNodes)
          or
           count(.|$vAncNodes) = count($vAncNodes)
          ]
      "/>
    
     <xsl:template match="/">
       <xsl:value-of select="count($vPrecInNodeSet) +1"/>
     </xsl:template>
    </xsl:stylesheet>
    

    :

    <root>
        <a>
            <b>zyx</b>
        </a>
        <a>
            <b>wvu</b>
        </a>
        <a>
            <b>tsr</b>
        </a>
        <a>
            <b>qpo</b>
        </a>
    </root>
    

    产生正确的结果

    3

    注意

        2
  •  2
  •   jasso    14 年前

    我想我有一个可行的解决方案

    其思想是计算文档中目标元素前面有多少个元素,并计算节点集中有多少个节点的前面元素较少或相等。在XPath中,这是:

    count(//a/b[count(./preceding::node()) &lt;= count(//a/b[.='tsr']/preceding::node())])
    

    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
        <xsl:output encoding="utf-8" method="text"/>
    
        <xsl:variable name="nodeset" select="//a/b"/>
        <xsl:variable name="path-string">//a/b</xsl:variable>
        <xsl:variable name="text">tsr</xsl:variable>
    
        <xsl:template match="/">
            <xsl:text>Find and print position of a node within a nodeset&#10;&#10;</xsl:text>
    
            <xsl:text>Position of "tsr" node in the nodeset = "</xsl:text>
            <xsl:value-of select="count(//a/b[count(./preceding::node()) &lt;= count(//a/b[.='tsr']/preceding::node()) ])"/>
            <xsl:text>"&#10;&#10;</xsl:text>
    
            <xsl:text>( Try the same using variables "$nodeset" and "$text" )&#10;</xsl:text>
            <xsl:text>Size of nodeset "$nodeset" = "</xsl:text>
            <xsl:value-of select="count($nodeset)"/>
            <xsl:text>"&#10;</xsl:text>
            <xsl:text>Variable "$text" = "</xsl:text>
            <xsl:value-of select="$text"/>
            <xsl:text>"&#10;</xsl:text>
            <xsl:text>Position of "</xsl:text>
            <xsl:value-of select="$text"/>
            <xsl:text>" node in the nodeset = "</xsl:text>
            <xsl:value-of select="count($nodeset[count(./preceding::node()) &lt;= count($nodeset[.=$text]/preceding::node()) ])"/>
            <xsl:text>"&#10;&#10;</xsl:text>
    
            <xsl:text>( Show that using a variable that has the path as a string does not work )&#10;</xsl:text>
            <xsl:text>Variable "$path-string" = "</xsl:text>
            <xsl:value-of select="$path-string"/>
            <xsl:text>"&#10;</xsl:text>
            <xsl:text>Result of "count($path-string)" = "</xsl:text>
            <xsl:value-of select="count($path-string)"/>
            <xsl:text>"&#10;&#10;</xsl:text>
    
            <xsl:text>End of tests&#10;</xsl:text>
        </xsl:template>
    
    </xsl:stylesheet>
    

    示例文档的输出

    Find and print position of a node within a nodeset
    
    Position of "tsr" node in the nodeset = "3"
    
    ( Try the same using variables "$nodeset" and "$text" )
    Size of nodeset "$nodeset" = "4"
    Variable "$text" = "tsr"
    Position of "tsr" node in the nodeset = "3"
    
    ( Show that using a variable that has the path as a string does not work )
    Variable "$path-string" = "//a/b"
    Result of "count($path-string)" = "1"
    
    End of tests
    

    我有

        3
  •  1
  •   Community rcollyer    7 年前

    在某些情况下,前面的(-sibling)答案的计数效果很好;您只需从所选项目的角度重新指定上下文节点集,然后应用 count(preceding:: )

    但在其他情况下,前面的计数确实很难保持在您想要处理的节点集中,正如您所暗示的那样。E、 假设您的工作节点集是/html/body/div[3]//a(所有 <a> 锚定在第三个 <div> 你想找到 a[@href="foo.html"] count(preceding::a) ,你会不小心数数的 <a> count(preceding-sibling::a) ,你不会得到所有的,因为 < 元素可以是任何级别的。

    preceding::a[ancestor::div[count(preceding-sibling::div) = 2]]

    但是,如果使用XSLT,下面的方法可以避免这些问题。如果可以指定工作节点集,则可以找到与提供的条件匹配的节点在其中的位置。而且不必指定节点集两次:

        <xsl:for-each select="/root/a/b">
            <xsl:if test=". = 'tsr'"><xsl:value-of select="position()"/></xsl:if>
        </xsl:for-each>
    

    上下文位置 “标识上下文项在正在处理的序列中的位置。”

    如果您不是在XSLT中工作,您处于什么环境中?这里可能有一个类似的构造,用于迭代外部XPath表达式的结果,在那里您可以维护自己的计数器(如果没有上下文位置可用),并根据内部条件测试每个项。

    为什么 other guy's attempt on the older question , a/b[.='tsr']/position()

        4
  •  0
  •   Richard    14 年前

    b 一中的节点 a

    而是需要在包含“a”的“b”之前查找前面的“a”节点的计数。

    比如:

    count(a[b[.='tsr']]/preceding-sibling::a)
    
        5
  •  0
  •   Jérôme Verstrynge Mark    13 年前

    从(即反对)根:

    count(//a/b[.='tsr']/preceding::b)
    

    如果你说另一个节点,例如:

    <c>
        <b>qqq</b>
    </c>
    

    想忽略所有没有“a”父母的b元素

    count(//a/b[.='tsr']/preceding::b[local-name(parent::node())='a'])
    

        6
  •  -1
  •   Tim C    14 年前

    这个怎么样。。

    count(a/b[.='tsr']/preceding-sibling::b) + count(a[b[.='tsr']]/preceding-sibling::a/b) + 1
    

    计算当前a元素中b元素以前的同级元素,然后计算a元素所有先前同级元素的b元素。或者类似的事情。