Найти положение узла с помощью xpath - PullRequest
84 голосов
/ 22 октября 2008

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

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

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

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

a/b[.='tsr']

Что хорошо, но я хочу вернуть порядковую позицию этого узла, что-то вроде:

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

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

Возможно ли это вообще?

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


Обновление : Завершено использованием Джеймс Сулак отличный ответ . Для тех, кто заинтересован, вот моя реализация на 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 ]

90 голосов
/ 22 октября 2008

Попробуйте:

count(a/b[.='tsr']/preceding-sibling::*)+1.
9 голосов
/ 22 октября 2008

Вы можете сделать это с 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>
7 голосов
/ 09 августа 2010

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

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

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

вместо

count(a/b[.='tsr']/preceding::*)+1.
3 голосов
/ 21 сентября 2015

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

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

Где:

  • 1-й параметр - последовательность поиска
  • 2-е что искать
3 голосов
/ 05 августа 2010

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

1 голос
/ 27 ноября 2014

Просто записка к ответу Джеймса Сулака.

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

count(a/b[.='tsr']/preceding-sibling::*)+number(boolean(a/b[.='tsr']))
0 голосов
/ 12 ноября 2008

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

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

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

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

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

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

0 голосов
/ 23 октября 2008

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

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

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;
                }
            }
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...