Trouver la position d'un nœud à l'aide de xpath

86

Quelqu'un sait-il comment obtenir la position d'un nœud en utilisant xpath?

Disons que j'ai le XML suivant:

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

Je peux utiliser la requête xpath suivante pour sélectionner le troisième <b> nœud (<b> tsr </b>):

a/b[.='tsr']

Ce qui est bien beau mais je veux retourner la position ordinale de ce nœud, quelque chose comme:

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

(mais un peu plus de travail!)

Est-ce même possible?

edit : J'ai oublié de mentionner que j'utilise .net 2, donc c'est xpath 1.0!


Mise à jour : Ended à l' aide de James Sulak de excellente réponse . Pour ceux qui sont intéressés, voici mon implémentation en 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
la source
Veuillez essayer de ne pas publier de réponses dans la question -> il serait préférable d'avoir posté ceci en tant que réponse, puis éventuellement lié à celle-ci à partir de la question.
theMayer

Réponses:

94

Essayer:

count(a/b[.='tsr']/preceding-sibling::*)+1.
James Sulak
la source
1
'Parce que j'utilise .net et soit lui, soit je ne peux pas gérer la puissance avec laquelle je suis allé: int position = doc.SelectNodes ("a / b [. =' Tsr '] / precedent-Sibling :: b") .Count + 1; if (position> 1 || doc.SelectSingleNode ("a / b [. = 'tsr']")! = null) // Vérifiez que le nœud existe réellement {// Faites de la magie ici}
Wilfred Knievel
dans zéro langues indexées, vous n'avez pas besoin du +1
JonnyRaa
9

Vous pouvez le faire avec XSLT mais je ne suis pas sûr de XPath pur.

<?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
la source
9

Je me rends compte que le poste est ancien .. mais ..

remplacer l'astérisque par le nom du nœud vous donnerait de meilleurs résultats

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

au lieu de

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

Si jamais vous mettez à niveau vers XPath 2.0, notez qu'il fournit un index de fonction , cela résout le problème de cette façon:

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

Où:

  • Le premier paramètre est la séquence de recherche
  • Le deuxième est ce qu'il faut rechercher
CroWell
la source
Il convient de noter que cela ne fonctionnera qu'avec XPath 2+. Tout ce qui se trouve en dessous devra utiliser la fonction de comptage «bizarre».
Dan Atkinson
1
@Dan, il a été noté dans le lien vers les documents originaux, ajout d'un avis explicite, merci!
CroWell
3

Contrairement à ce qui a été dit précédemment, «précédent-frère» est vraiment l'axe à utiliser, et non «précédent» qui fait quelque chose de complètement différent, il sélectionne tout dans le document qui est avant la balise de début du nœud courant. (voir http://www.w3schools.com/xpath/xpath_axes.asp )

Damien
la source
4
Non compris les nœuds ancêtres. Ne faites pas confiance à w3schools pour les détails! Mais je suis d'accord ... bien que précédant :: fonctionne dans ce cas, car il n'y a pas d'éléments avant les éléments b pertinents autres que l'ancêtre a, c'est plus fragile que le frère précédent. OTOH, l'OP ne nous a pas dit dans quel contexte il voulait connaître la position à l'intérieur, donc potentiellement précéder :: pourrait être correct.
LarsH
2

Juste une note à la réponse faite par James Sulak.

Si vous souhaitez prendre en compte le fait que le nœud peut ne pas exister et que vous souhaitez le conserver purement XPATH, essayez ce qui suit qui renverra 0 si le nœud n'existe pas.

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

Le problème est que la position du nœud ne signifie pas grand-chose sans contexte.

Le code suivant vous donnera l'emplacement du nœud dans ses nœuds enfants parents

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
la source
1
Donc pas vraiment un chercheur XPath maintenant, mais un chercheur C #.
jamesh
0

Je fais beaucoup de choses sur Novell Identity Manager, et XPATH dans ce contexte est un peu différent.

Supposons que la valeur que vous recherchez se trouve dans une variable de chaîne, appelée TARGET, alors XPATH serait:

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

De plus, il a été souligné que pour économiser quelques caractères d'espace, les éléments suivants fonctionneraient également:

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

J'ai également publié une version plus jolie de ceci sur Cool Solutions de Novell: Utilisation de XPATH pour obtenir le nœud de position

geoffc
la source