Temukan posisi node menggunakan xpath

86

Ada yang tahu cara mendapatkan posisi node menggunakan xpath?

Katakanlah saya memiliki xml berikut:

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

Saya dapat menggunakan kueri xpath berikut untuk memilih <b> node (<b> tsr </b>) ketiga:

a/b[.='tsr']

Yang mana semuanya baik dan bagus tetapi saya ingin mengembalikan posisi ordinal dari simpul itu, seperti:

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

(tapi sedikit lebih berhasil!)

Apakah itu mungkin?

edit : Lupa menyebutkan saya menggunakan .net 2 jadi xpath 1.0!


Pembaruan : Akhirnya menggunakan jawaban yang sangat baik dari James Sulak . Bagi yang tertarik, inilah implementasi saya di 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
sumber
Harap coba untuk tidak memposting jawaban dalam pertanyaan -> akan lebih baik jika memposting ini sebagai jawaban, kemudian mungkin ditautkan dari pertanyaan.
theMayer

Jawaban:

95

Mencoba:

count(a/b[.='tsr']/preceding-sibling::*)+1.
James Sulak
sumber
1
Karena saya menggunakan .net & entah itu atau saya tidak dapat menangani kekuatan yang saya gunakan: int position = doc.SelectNodes ("a / b [. = 'Tsr'] / preceding-Sibling :: b") .Hitung + 1; if (position> 1 || doc.SelectSingleNode ("a / b [. = 'tsr']")! = null) // Periksa apakah node tersebut benar-benar ada {// Lakukan keajaiban di sini}
Wilfred Knievel
dalam bahasa nol indeks Anda tidak perlu +1
JonnyRaa
9

Anda dapat melakukan ini dengan XSLT tetapi saya tidak yakin tentang XPath langsung.

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

Saya menyadari bahwa posting itu kuno .. tapi ..

mengganti asterisk dengan nama noden akan memberikan hasil yang lebih baik

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

dari pada

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

Jika Anda pernah memutakhirkan ke XPath 2.0, perhatikan bahwa ini menyediakan indeks fungsi , ini menyelesaikan masalah dengan cara ini:

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

Dimana:

  • Parameter pertama adalah urutan pencarian
  • Kedua adalah apa yang dicari
CroWell
sumber
Perlu dicatat bahwa ini hanya akan bekerja dengan XPath 2+. Apa pun di bawah itu harus menggunakan fungsi hitung 'aneh'.
Dan Atkinson
1
@Dan, itu tercatat di tautan ke dokumen asli, menambahkan pemberitahuan eksplisit, terima kasih!
CroWell
3

Tidak seperti yang dinyatakan sebelumnya, 'preceding-sibling' sebenarnya adalah sumbu untuk digunakan, bukan 'preceding' yang melakukan sesuatu yang sangat berbeda, ia memilih semua yang ada di dokumen sebelum tag awal dari node saat ini. (lihat http://www.w3schools.com/xpath/xpath_axes.asp )

Damien
sumber
4
Tidak termasuk node leluhur. Jangan percaya w3schools detailnya! Tapi saya setuju ... meskipun dalam kasus ini: preceding :: works, karena tidak ada elemen sebelum elemen b yang relevan selain sebuah leluhur, itu lebih rapuh daripada saudara sebelumnya. OTOH, OP tidak memberi tahu kami konteks apa yang ingin dia ketahui posisi di dalamnya, jadi berpotensi sebelumnya :: bisa benar.
LarsH
2

Sekadar catatan atas jawaban yang dilakukan oleh James Sulak.

Jika Anda ingin mempertimbangkan bahwa node mungkin tidak ada dan ingin menyimpannya murni XPATH, maka coba berikut ini yang akan mengembalikan 0 jika node tidak ada.

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

Masalahnya adalah posisi node tidak berarti banyak tanpa konteks.

Kode berikut akan memberi Anda lokasi node di node anak induknya

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
sumber
1
Jadi bukan benar-benar pencari XPath sekarang, tapi pencari C #.
jamesh
0

Saya melakukan banyak hal tentang Manajer Identitas Novell, dan XPATH dalam konteks itu terlihat sedikit berbeda.

Asumsikan nilai yang Anda cari ada dalam variabel string, disebut TARGET, maka XPATH adalah:

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

Selain itu, disebutkan bahwa untuk menghemat beberapa karakter ruang, berikut ini juga akan berfungsi:

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

Saya juga memposting versi yang lebih cantik dari ini di Novell's Cool Solutions: Menggunakan XPATH untuk mendapatkan node posisi

geoffc
sumber