2008-10-22 2 views
79

Кто-нибудь знает, как получить позицию узла с помощью xpath?Найти местоположение узла с помощью 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() 

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

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

Редактировать: Забыл упомянуть, используя .net 2, так что это xpath 1.0!


Update: Законченное использованием James Sulak «s excellent answer. Для тех, кто заинтересован, вот моя реализация в 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); 
} 

ответ

84

Try:

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

'Coz Я использую .net, и я либо не могу справиться с властью, с которой я пошел: int position = doc.SelectNodes ("a/b [. =' Tsr ']/previous-Sibling :: b ") .Count + 1; if (position> 1 || doc.SelectSingleNode ("a/b [. = 'tsr']")! = null) // Проверяем, что узел фактически существует {// Здесь делает магию} – 2008-10-24 09:52:59

+0

в нуль проиндексированных языках, t нужно +1 – 2017-11-23 14:50:30

8

Вы можете сделать это с помощью 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> 
1

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

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

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

Так что на самом деле это не XPath finder, а C# finder. – jamesh 2008-10-29 13:03:12

0

я много вещей Novell Identity Manager и XPATH в этом контексте выглядит несколько иначе.

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

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

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

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

Я также разместил покрасивее версию этого в Cool Solutions компании Novell: Using XPATH to get the position node

3

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

+4

Не включая узлы предков. Не доверяйте w3schools по деталям! Но я согласен ... хотя предыдущий :: работает в этом случае, потому что нет элементов перед соответствующими элементами b, отличными от предка, он более хрупкий, чем предыдущий-сиблинг. OTOH, ОП не рассказал нам, в каком контексте он хотел бы знать позицию внутри, поэтому потенциально предшествующий :: может быть прав. – LarsH 2010-08-18 18:44:32

5

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

replace'ing звездочка с имя_узлом даст вам лучшие результаты

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

вместо

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

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

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

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

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

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

Где:

  • первый параметр является последовательность для поиска
  • 2-ое, что искать
Смежные вопросы