Find position of a node using XPath

118,354

Solution 1

Try:

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

Solution 2

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>

Solution 3

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.

Solution 4

If you ever upgrade to XPath 2.0, note that it provides function index-of, it solves problem this way:

index-of(//b, //b[.='tsr'])

Where:

  • 1st parameter is sequence for searching
  • 2nd is what to search

Solution 5

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)

Share:
118,354
Wilfred Knievel
Author by

Wilfred Knievel

  

Updated on April 02, 2021

Comments

  • Wilfred Knievel
    Wilfred Knievel over 3 years

    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);
    }
    
  • Wilfred Knievel
    Wilfred Knievel over 15 years
    'Coz I'm using .net & either it or I can't handle the power I went with: int position = doc.SelectNodes("a/b[.='tsr']/preceding-Sibling::b").Count + 1; if (position > 1 || doc.SelectSingleNode("a/b[.='tsr']") != null) // Check the node actually exists { // Do magic here }
  • Ishyc
    Ishyc over 15 years
    So not really an XPath finder now, but a C# finder.
  • LarsH
    LarsH almost 14 years
    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.
  • Dan Atkinson
    Dan Atkinson over 8 years
    It should be noted that this will only work with XPath 2+. Anything below that will have to use the 'weird' count function.
  • CroWell
    CroWell over 8 years
    @Dan, it was noted in the link to original docs, added explicit notice, thanks!
  • JonnyRaa
    JonnyRaa over 6 years
    in zero indexed languages you don't need the +1
  • Onyr
    Onyr over 3 years
    A little example in-situ (get the position of the element France in a XML doc) : index-of(//country_name/common_name, //country_name/common_name[text()="France"])