Найти позицию node с помощью xpath

Кто-нибудь знает, как получить позицию node с помощью xpath?

Скажем, у меня есть следующий xml:

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

Я могу использовать следующий запрос xpath для выбора третьего <b> node (<b> tsr </b> ):

a/b[.='tsr']

Что все хорошо и хорошо, но я хочу вернуть порядковое положение этого node, что-то вроде:

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

(но немного больше работает!)

Возможно ли это?

edit: Забыл упомянуть, что я использую .net 2, поэтому он xpath 1.0!


Обновление: закончено с помощью Джеймса Сулака отличный ответ. Для тех, кто интересуется здесь, моя реализация в С#:

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

Ответ 1

Try:

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

Ответ 2

Вы можете сделать это с помощью XSLT, но я не уверен в прямом 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>

Ответ 3

Я понимаю, что пост древний.. но..

замена звездочки на nodename даст вам лучшие результаты

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

вместо

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

Ответ 4

В отличие от предыдущего ранее "предшествующего брака" на самом деле является осью, а не "предшествующей", которая делает что-то совершенно другое, она выбирает все в документе, который находится перед началом тега текущего node. (см. http://www.w3schools.com/xpath/xpath_axes.asp)

Ответ 5

Проблема в том, что позиция node не имеет большого значения без контекста.

Следующий код предоставит вам местоположение node в родительских дочерних узлах

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

Ответ 6

Просто примечание к ответу, сделанному Джеймсом Сулаком.

Если вы хотите принять во внимание, что node может не существовать и хотите сохранить его просто XPATH, попробуйте следующее, которое вернет 0, если node не существует.

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

Ответ 7

Если вы когда-либо обновляетесь до XPath 2.0, обратите внимание, что он предоставляет функцию index-of, она решает проблему следующим образом:

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

Где:

  • 1-й параметр - это последовательность для поиска
  • 2nd - это то, что искать

Ответ 8

Я занимаюсь большим количеством материалов Novell Identity Manager, и XPATH в этом контексте выглядит немного по-другому.

Предположим, что значение, которое вы ищете, находится в строковой переменной, называемой TARGET, тогда XPATH будет:

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

Кроме того, было указано, что для сохранения нескольких символов пробела будет работать следующее:

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

Я также опубликовал более красивую версию этого в Novell Cool Solutions: Использование XPATH для получения позиции node