使用xpath查找节点在节点集中的位置 [英] Find position of a node within a nodeset using 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屋!