Finden Sie die Position eines Knotens mit xpath

86

Weiß jemand, wie man mit xpath die Position eines Knotens ermittelt?

Angenommen, ich habe die folgende XML:

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

Ich kann die folgende xpath-Abfrage verwenden, um den dritten <b> -Knoten (<b> tsr </ b>) auszuwählen:

a/b[.='tsr']

Welches ist alles schön und gut , aber ich möchte zurückkehren , die Ordnungsposition des Knotens, so etwas wie:

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

(aber ein bisschen mehr arbeiten!)

Ist es überhaupt möglich?

edit : Ich habe vergessen zu erwähnen, dass ich .net 2 benutze, also ist es xpath 1.0!


Update : Beendet mit bis James Sulak ‚s ausgezeichnete Antwort . Für diejenigen, die interessiert sind, hier ist meine Implementierung 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
quelle
Bitte versuchen Sie, keine Antworten in der Frage zu posten -> es wäre besser, diese als Antwort zu posten und dann möglicherweise aus der Frage darauf zu verlinken.
theMayer

Antworten:

94

Versuchen:

count(a/b[.='tsr']/preceding-sibling::*)+1.
James Sulak
quelle
1
'Coz ich benutze .net & entweder es oder ich kann nicht mit der Leistung umgehen, mit der ich gegangen bin: int position = doc.SelectNodes ("a / b [. =' Tsr '] / previous-Sibling :: b") .Count + 1; if (position> 1 || doc.SelectSingleNode ("a / b [. = 'tsr']")! = null) // Überprüfe, ob der Knoten tatsächlich existiert {// Magie hier}}
Wilfred Knievel
in null indizierten Sprachen brauchen Sie nicht die +1
JonnyRaa
9

Sie können dies mit XSLT tun, aber ich bin mir bei XPath nicht sicher.

<?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>
Steven Huwig
quelle
9

Mir ist klar, dass die Post uralt ist .. aber ..

Wenn Sie das Sternchen durch den Knotennamen ersetzen, erhalten Sie bessere Ergebnisse

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

anstatt

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

Wenn Sie jemals auf XPath 2.0 aktualisieren, beachten Sie, dass es einen Funktionsindex von bietet. Es löst das Problem auf folgende Weise:

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

Wo:

  • Der erste Parameter ist die Sequenz für die Suche
  • 2. ist was zu suchen
CroWell
quelle
Es ist zu beachten, dass dies nur mit XPath 2+ funktioniert. Alles, was darunter liegt, muss die 'seltsame' Zählfunktion verwenden.
Dan Atkinson
1
@ Dan, es wurde im Link zu den Originaldokumenten vermerkt, expliziter Hinweis hinzugefügt, danke!
CroWell
3

Anders als zuvor angegeben, ist "Vorgänger-Geschwister" tatsächlich die zu verwendende Achse, nicht "Vorgänger", was etwas völlig anderes bewirkt. Es wählt alles im Dokument aus, das sich vor dem Start-Tag des aktuellen Knotens befindet. (siehe http://www.w3schools.com/xpath/xpath_axes.asp )

Damien
quelle
4
Ohne Ahnenknoten. Vertraue w3schools nicht auf die Details! Aber ich stimme zu ... obwohl vorangestelltes :: in diesem Fall funktioniert, da es vor den relevanten b-Elementen außer dem a-Vorfahren keine Elemente gibt, ist es fragiler als vorangegangenes Geschwister. OTOH, das OP hat uns nicht gesagt, in welchem ​​Kontext er die Position innerhalb wissen wollte, also könnte möglicherweise vorangestelltes :: richtig sein.
LarsH
2

Nur eine Anmerkung zur Antwort von James Sulak.

Wenn Sie berücksichtigen möchten, dass der Knoten möglicherweise nicht vorhanden ist, und ihn lediglich als XPATH beibehalten möchten, versuchen Sie Folgendes, das 0 zurückgibt, wenn der Knoten nicht vorhanden ist.

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

Das Problem ist, dass die Position des Knotens ohne Kontext nicht viel bedeutet.

Der folgende Code gibt Ihnen die Position des Knotens in seinen übergeordneten untergeordneten Knoten an

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;
                }
            }
        }
    }
}
Andrew Cox
quelle
1
Also jetzt nicht wirklich ein XPath-Finder, sondern ein C # -Finder.
Jamesh
0

Ich mache viele Novell Identity Manager-Sachen, und XPATH sieht in diesem Zusammenhang etwas anders aus.

Angenommen, der gesuchte Wert befindet sich in einer Zeichenfolgenvariablen namens TARGET. Dann wäre der XPATH:

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

Zusätzlich wurde darauf hingewiesen, dass zum Speichern einiger Zeichen Platz auch Folgendes funktionieren würde:

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

Ich habe auch eine schönere Version davon bei Novell's Cool Solutions veröffentlicht: Verwenden von XPATH, um den Positionsknoten zu erhalten

geoffc
quelle