Find position of a node using XPath

Viewed 120797

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!


Update: Ended up using James Sulak's excellent answer. For those that are interested here's my implementation in C#:

int position = doc.SelectNodes("a/b[.='tsr']/preceding-sibling::b").Count + 1;

// Check the node actually exists
if (position > 1 || doc.SelectSingleNode("a/b[.='tsr']") != null)
{
    Console.WriteLine("Found at position = {0}", position);
}
8 Answers

Try:

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

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

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

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

Related