使用xpath查找节点在节点集中的位置 [英] Find position of a node within a nodeset using xpath

查看:232
本文介绍了使用xpath查找节点在节点集中的位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

徒劳地玩弄position()之后,我一直在寻找解决方案,然后来到解决方案

以下是 general 解决方案,该解决方案适用于同一文档中属于节点的任何节点集的任何节点:

我正在使用XSLT来实现该解决方案,但最终获得了一个可以与任何其他托管语言一起使用的XPath表达式.

$vNodeSet是节点集,而$vNode是该节点集中我们要查找其位置的节点.

然后,让$vPrecNodes包含$vNode之前的XML文档中的所有节点.

然后,让$vAncNodes包含XML文档中所有作为$vNode祖先的节点.

按文档顺序在$vNode之前的$vNodeSet中的节点集由节点集中的所有节点也属于$vPrecNodes和节点集中的所有节点也属于$vAncNodes. /p>

对于两个节点集的交集,我将使用众所周知的Kaysian公式:

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

精确地包含$ns1$ns2的交点中的节点.

基于所有这些,令$vPrecInNodeSet$vNodeSet中按文档顺序在$vNode之前的节点集.以下XPath表达式定义$vPrecInNodeSet:

$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>

将上述转换应用于提供的XML文档:

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

产生了正确的结果:

3

注意:此解决方案不依赖XSLT(仅用于说明目的).您可以组装一个XPath表达式,用变量定义替换它们,直到没有更多变量要替换为止.

After playing around with position() in vain I was googling around for a solution and arrived at this older stackoverflow question which almost describes my problem.

The difference is that the nodeset I want the position within is dynamic, rather than a contiguous section of the document.

To illustrate I'll modify the example from the linked question to match my requirements. Note that each <b> element is within a different <a> element. This is the critical bit.

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

Now if I queried, using XPath for a/b I'd get a nodeset of the four <b> nodes. I want to then find the position within that nodeset of the node that contains the string 'tsr'. The solution in the other post breaks down here: count(a/b[.='tsr']/preceding-sibling::*)+1 returns 1 because preceding-sibling is navigating the document rather than the context node-set.

Is it possible to work within the context nodeset?

解决方案

Here is a general solution that works on any node that belongs in any node-set of nodes in the same document:

I am using XSLT to implement the solution, but finally obtain a single XPath expression that may be used with any other hosting language.

Let $vNodeSet be the node-set and $vNode be the node in this node-set whose position we want to find.

Then, let $vPrecNodes contains all nodes in the XML document preceding $vNode.

Then, let $vAncNodes contains all nodes in the XML document that are ancestors of $vNode.

The set of nodes in $vNodeSet that precede $vNode in document order consists of all nodes in the nodeset that belong also to $vPrecNodes and all nodes in the node-set that also belong to $vAncNodes.

I will use the well-known Kaysian formula for intersection of two nodesets:

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

contains exactly the nodes in the intersection of $ns1 with $ns2.

Based on all this, let $vPrecInNodeSet is the set of nodes in $vNodeSet that precede $vNode in document order. The following XPath expression defines $vPrecInNodeSet:

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

Finally, the wanted position is: count($vPrecInNodeSet) +1

Here's how this all works together:

<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>

When the above transformation is applied on the provided XML document:

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

the correct result is produced:

3

Do note: This solution does not depend on XSLT (used only for illustrative purposes). You may assemble a single XPath expression, substituting the variables with their definition, until there are no more variables to substitute.

这篇关于使用xpath查找节点在节点集中的位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆