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.