Trova la posizione di un nodo usando xpath


86

Qualcuno sa come ottenere la posizione di un nodo usando xpath?

Diciamo che ho il seguente xml:

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

Posso utilizzare la seguente query xpath per selezionare il terzo <b> nodo (<b> tsr </b>):

a/b[.='tsr']

Il che va benissimo, ma voglio restituire la posizione ordinale di quel nodo, qualcosa del tipo:

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

(ma un po 'più funzionante!)

È anche possibile?

modifica : ho dimenticato di menzionare che sto usando .net 2 quindi è xpath 1.0!


Aggiornamento : finito per usare James Sulak 's risposta eccellente . Per coloro che sono interessati, ecco la mia implementazione 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);
}

Si prega di cercare di non pubblicare risposte nella domanda -> sarebbe meglio averla pubblicata come risposta, quindi possibilmente collegata ad essa dalla domanda.
theMayer

Risposte:


94

Provare:

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

1
'Perché sto usando .net e o non posso gestire la potenza con cui sono andato: int position = doc.SelectNodes ("a / b [. =' Tsr '] / preceding-Sibling :: b") .Count + 1; if (position> 1 || doc.SelectSingleNode ("a / b [. = 'tsr']")! = null) // Controlla che il nodo esista effettivamente {// Fai magie qui}
Wilfred Knievel

in zero lingue indicizzate non è necessario il +1
JonnyRaa

9

Puoi farlo con XSLT ma non sono sicuro di XPath diretto.

<?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>

9

Mi rendo conto che il post è antico .. ma ..

sostituire l'asterisco con il nome del nodo darebbe risultati migliori

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

invece di

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

4

Se esegui l'aggiornamento a XPath 2.0, nota che fornisce la funzione index-of , risolve il problema in questo modo:

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

Dove:

  • Il primo parametro è la sequenza per la ricerca
  • Il secondo è cosa cercare

Va notato che funziona solo con XPath 2+. Qualsiasi cosa sotto dovrà usare la funzione di conteggio "strano".
Dan Atkinson

1
@ Dan, è stato notato nel link ai documenti originali, aggiunto avviso esplicito, grazie!
CroWell

3

A differenza di quanto affermato in precedenza, "preceding-sibling" è in realtà l'asse da usare, non "preceding" che fa qualcosa di completamente diverso, seleziona tutto nel documento che si trova prima del tag di inizio del nodo corrente. (vedi http://www.w3schools.com/xpath/xpath_axes.asp )


4
Esclusi i nodi antenati. Non fidarti di w3schools sui dettagli! Ma sono d'accordo ... anche se preceding :: funziona in questo caso, perché non ci sono elementi prima degli elementi b rilevanti oltre all'antenato a, è più fragile del precedente-fratello. OTOH, l'OP non ci ha detto in quale contesto voleva conoscere la posizione all'interno, quindi potenzialmente precedente :: potrebbe essere giusto.
LarsH

2

Solo una nota alla risposta data da James Sulak.

Se vuoi tenere in considerazione che il nodo potrebbe non esistere e vuoi mantenerlo puramente XPATH, prova quanto segue che restituirà 0 se il nodo non esiste.

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

0

Il problema è che la posizione del nodo non significa molto senza un contesto.

Il codice seguente ti fornirà la posizione del nodo nei suoi nodi figlio padre

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
Quindi non proprio un XPath finder ora, ma un C # finder.
jamesh

0

Faccio molte cose su Novell Identity Manager e XPATH in quel contesto ha un aspetto leggermente diverso.

Supponiamo che il valore che stai cercando sia in una variabile stringa, chiamata TARGET, quindi XPATH sarebbe:

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

Inoltre è stato sottolineato che per salvare alcuni caratteri di spazio, funzionerebbe anche quanto segue:

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

Ho anche pubblicato una versione più carina di questo su Novell's Cool Solutions: Using XPATH per ottenere il nodo di posizione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.