tags:

views:

6094

answers:

6

Anyone know how to get the position of a node using xpath?

Say I have the following xml:

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

I can use the following xpath query to select the third <b> node (<b>tsr</b>):

a/b[.='tsr']

Which is all well and good but I want to return the ordinal position of that node, something like:

a/b[.='tsr']/position()

(but a bit more working!)

Is it even possible?

edit: Forgot to mention am using .net 2 so it's xpath 1.0!

+3  A: 

You can do this with XSLT but I'm not sure about straight XPath.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="xml" encoding="utf-8" indent="yes" 
              omit-xml-declaration="yes"/>
  <xsl:template match="a/*[text()='tsr']">
    <xsl:number value-of="position()"/>
  </xsl:template>
  <xsl:template match="text()"/>
</xsl:stylesheet>
Steven Huwig
+11  A: 

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

James Sulak
'Coz I'm using .net if (position > 1 || doc.SelectSingleNode("a/b[.='tsr']") != null) // Check the node actually exists { // Do magic here }
Wilfred Knievel
A: 

The problem is that the position of the node doesn't mean much without a context.

The following code will give you the location of the node in its parent child nodes

using System;
using System.Xml;

public class XpathFinder
{
    public static void Main(string[] args)
    {
        XmlDocument xmldoc = new XmlDocument();
        xmldoc.Load(args[0]);
        foreach ( XmlNode xn in xmldoc.SelectNodes(args[1]) )
        {
            for (int i = 0; i < xn.ParentNode.ChildNodes.Count; i++)
            {
                if ( xn.ParentNode.ChildNodes[i].Equals( xn ) )
                {
                    Console.Out.WriteLine( i );
                    break;
                }
            }
        }
    }
}
Andrew Cox
So not really an XPath finder now, but a C# finder.
jamesh
A: 

I do a lot of Novell Identity Manager stuff, and XPATH in that context looks a little different.

Assume the value you are looking for is in a string variable, called TARGET, then the XPATH would be:

count(attr/value[.='$TARGET']/preceding-sibling::*)+1

Additionally it was pointed out that to save a few characters of space, the following would work as well:

count(attr/value[.='$TARGET']/preceding::*) + 1

I also posted a prettier version of this at Novell's Cool Solutions: Using XPATH to get the position node

geoffc
A: 

Unlike stated previously 'preceding-sibling' is really the axis to use, not 'preceding' which does something completely different, it selects everything in the document that is before the start tag of the current node. (see http://www.w3schools.com/xpath/xpath_axes.asp)

Damien
Not including ancestor nodes. Don't trust w3schools on the details!But I agree... although preceding:: works in this case, because there are no elements before the relevant b elements other than the a ancestor, it's more fragile than preceding-sibling.OTOH, the OP didn't tell us what context he wanted to know the position within, so potentially preceding:: could be right.
LarsH
+1  A: 

I realize that the post is ancient.. but..

replace'ing the asterisk with the nodename would give you better results

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

instead of

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