XPath berisi (teks (), 'beberapa string') tidak berfungsi bila digunakan dengan simpul dengan lebih dari satu Subnode teks

259

Saya punya masalah kecil dengan Xpath berisi dengan dom4j ...

Katakanlah XML saya

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

Katakanlah saya ingin menemukan semua node yang memiliki ABC dalam teks yang diberikan Elemen root ...

Jadi xpath yang harus saya tulis adalah

//*[contains(text(),'ABC')]

Namun ini bukan apa yang dikembalikan Dom4j .... apakah ini masalah dom4j atau pemahaman saya tentang cara kerja xpath. karena kueri itu hanya mengembalikan Elemen Jalan dan bukan elemen Komentar.

DOM menjadikan elemen Komentar elemen gabungan dengan empat tag dua

[Text = 'XYZ'][BR][BR][Text = 'ABC'] 

Saya akan berasumsi bahwa kueri masih harus mengembalikan elemen karena harus menemukan elemen dan menjalankan mengandung di atasnya tetapi tidak ... ...

kueri berikut mengembalikan elemen tetapi mengembalikan jauh lebih dari elemen, itu mengembalikan elemen induk juga ... yang tidak diinginkan untuk masalah ...

//*[contains(text(),'ABC')]

Apakah ada yang tahu permintaan xpath yang hanya akan mengembalikan Elemen <Street/>dan <Comment/>?

Mike Milkin
sumber
Sejauh yang saya tahu, //*[contains(text(),'ABC')]hanya mengembalikan <Street>elemen. Itu tidak mengembalikan leluhur <Street>atau <Comment>.
Ken Bloom

Jawaban:

707

The <Comment>tag berisi dua node teks dan dua <br>node sebagai anak-anak.

Ekspresi xpath Anda tadinya

//*[contains(text(),'ABC')]

Untuk memecah ini,

  1. * adalah pemilih yang cocok dengan elemen apa pun (yaitu tag) - ia mengembalikan set-simpul.
  2. Ini []adalah kondisi yang beroperasi pada setiap node individu di set simpul itu. Ini cocok jika ada satu node yang beroperasi sesuai kondisi di dalam tanda kurung.
  3. text()adalah pemilih yang cocok dengan semua node teks yang merupakan anak-anak dari simpul konteks - mengembalikan set simpul.
  4. containsadalah fungsi yang beroperasi pada string. Jika melewati set simpul, set simpul dikonversi menjadi string dengan mengembalikan nilai string dari simpul dalam set-simpul yang pertama dalam urutan dokumen . Oleh karena itu, ia hanya dapat mencocokkan simpul teks pertama dalam <Comment>elemen Anda - yaitu BLAH BLAH BLAH. Karena itu tidak cocok, Anda tidak mendapatkan <Comment>hasil Anda.

Anda perlu mengubahnya ke

//*[text()[contains(.,'ABC')]]
  1. * adalah pemilih yang cocok dengan elemen apa pun (yaitu tag) - ia mengembalikan set-simpul.
  2. Bagian luar []adalah suatu kondisi yang beroperasi pada setiap node individu dalam set simpul - di sini beroperasi pada setiap elemen dalam dokumen.
  3. text()adalah pemilih yang cocok dengan semua node teks yang merupakan anak-anak dari simpul konteks - mengembalikan set simpul.
  4. Bagian dalam []adalah sebuah kondisi yang beroperasi pada setiap node di set simpul itu - di sini setiap node teks individu. Setiap node teks individu adalah titik awal untuk setiap jalur dalam tanda kurung, dan juga dapat disebut secara eksplisit sebagai .dalam tanda kurung. Ini cocok jika ada satu node yang beroperasi sesuai kondisi di dalam tanda kurung.
  5. containsadalah fungsi yang beroperasi pada string. Di sini dilewatkan node teks individu ( .). Karena dilewatkan node teks kedua dalam <Comment>tag secara individual, ia akan melihat 'ABC'string dan dapat mencocokkannya.
Ken Bloom
sumber
1
Hebat im sedikit xob noob, jadi biar saya dapatkan ini, text () adalah fungsi yang mengambil ekspresi berisi (., 'ABC'), Apakah ada kemungkinan Anda bisa menjelaskan jadi saya tidak melakukan ini agak hal-hal bodoh lagi;)
Mike Milkin
28
Saya sudah mengedit jawaban saya untuk memberikan penjelasan panjang. Saya sendiri tidak begitu tahu tentang XPath - saya hanya bereksperimen sedikit sampai saya menemukan kombinasi itu. Setelah saya memiliki kombinasi yang berfungsi, saya menebak apa yang sedang terjadi dan mencari dalam standar XPath untuk mengkonfirmasi apa yang saya pikir sedang terjadi dan menulis penjelasannya.
Ken Bloom
2
Bagaimana Anda membuat ini pencarian kasus tidak sensitif?
Zack
@Zack: Tolong buat ini pertanyaan baru.
user1129682
1
Saya tahu ini adalah utas lama, tetapi adakah yang bisa berkomentar jika ada perbedaan mendasar, lebih disukai dengan beberapa kasus uji sederhana antara jawaban yang diberikan oleh Ken Bloom dan //*[contains(., 'ABC')]. Saya selalu menggunakan pola yang diberikan oleh Mike Milkin, berpikir itu lebih tepat, tetapi hanya melakukan containspada konteks saat ini tampaknya sebenarnya menjadi apa yang saya inginkan lebih sering.
Knickum
7

[contains(text(),'')]hanya mengembalikan benar atau salah. Itu tidak akan mengembalikan hasil elemen apa pun.

Ratna
sumber
ini tidak akan berhasil jika saya punya '' atau '' bagaimana kita bisa memotong?
shareef
contains(text(),'JB-')tidak bekerja! conatainsmembutuhkan dua string sebagai argumen - contains(**string**, **string**)! text () bukan string , adalah fungsi!
AtachiShadow
6

Dokumen XML:

<Home>
    <Addr>
        <Street>ABC</Street>
        <Number>5</Number>
        <Comment>BLAH BLAH BLAH <br/><br/>ABC</Comment>
    </Addr>
</Home>

Ekspresi XPath:

//*[contains(text(), 'ABC')]

//*cocok dengan elemen turunan dari node root . Artinya, elemen apa pun kecuali simpul root.

[...]adalah predikat , itu menyaring node-set. Ini mengembalikan node yang ...adalah true:

Predikat menyaring simpul-set [...] untuk menghasilkan simpul-set baru. Untuk setiap node di set simpul yang akan difilter, PredicateExpr dievaluasi [...]; jika PredicateExpr mengevaluasi ke true untuk simpul itu, simpul tersebut termasuk dalam simpul-set yang baru; jika tidak, itu tidak termasuk.

contains('haystack', 'needle')kembali truejika haystack berisi needle :

Fungsi: mengandung boolean (string, string)

Fungsi berisi mengembalikan true jika string argumen pertama berisi string argumen kedua, dan sebaliknya mengembalikan false.

Tetapi contains()mengambil string sebagai parameter pertama. Dan melewati node. Untuk mengatasinya, setiap node atau node-set dilewatkan sebagai parameter pertama dikonversi ke string oleh string()fungsi:

Argumen dikonversi untuk mengetikkan string seolah-olah dengan memanggil fungsi string.

string()pengembalian fungsi string-valuedari simpul pertama :

Node-set dikonversi ke string dengan mengembalikan nilai-string dari node dalam node-set yang pertama kali dalam urutan dokumen. Jika simpul-set kosong, string kosong dikembalikan.

string-valuedari simpul elemen :

Nilai string dari suatu simpul elemen adalah gabungan dari nilai-nilai string dari semua turunan simpul teks dari simpul elemen dalam urutan dokumen.

string-valuedari simpul teks :

Nilai string dari simpul teks adalah data karakter.

Jadi, pada dasarnya string-valueadalah semua teks yang terkandung dalam sebuah simpul (gabungan dari semua simpul teks turunan).

text() adalah tes simpul yang cocok dengan simpul teks apa pun:

Teks tes simpul () benar untuk setiap simpul teks. Misalnya, child :: text () akan memilih simpul teks anak-anak dari simpul konteks.

Karena itu, //*[contains(text(), 'ABC')]cocok dengan elemen apa pun (kecuali simpul root), simpul teks pertama yang berisi ABC. Sejak text()mengembalikan set-simpul yang berisi semua simpul teks turunan dari simpul konteks (relatif terhadap ekspresi yang dievaluasi). Tetapi contains()hanya mengambil yang pertama. Jadi untuk dokumen di atas jalan cocok dengan Streetelemen.

Ekspresi berikut //*[text()[contains(., 'ABC')]]cocok dengan elemen apa pun (tetapi simpul root), yang memiliki setidaknya satu simpul teks anak, yang berisi ABC. .mewakili node konteks. Dalam hal ini, ini adalah simpul teks anak dari elemen apa pun kecuali simpul akar. Jadi untuk dokumen di atas jalan cocok dengan Street, dan Commentelemen.

Nah, //*[contains(., 'ABC')]cocok dengan elemen apa pun (kecuali simpul root) yang berisi ABC(dalam rangkaian simpul teks turunan). Untuk dokumen di atas cocok dengan Home, yang Addr, yang Street, dan Commentunsur-unsur. Dengan demikian, //*[contains(., 'BLAH ABC')]cocok Homedengan Addr,, dan Commentelemen.

x-yuri
sumber
0

Butuh beberapa saat, tetapi akhirnya saya tahu. Xpath khusus yang berisi beberapa teks di bawah ini berfungsi dengan baik untuk saya.

//a[contains(text(),'JB-')]
zagoo2000
sumber
2
contains(text(),'JB-')tidak bekerja! conatainsmembutuhkan dua string sebagai argumen - contains(**string**, **string**)! text () bukan string , adalah fungsi!
AtachiShadow
0

Jawaban yang diterima akan mengembalikan semua node induk juga. Untuk mendapatkan hanya simpul yang sebenarnya dengan ABC meskipun stringnya adalah setelah
:

//*[text()[contains(.,'ABC')]]/text()[contains(.,"ABC")]
Roger Veciana
sumber
0
//*[text()='ABC'] 

kembali

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>
pengguna3520544
sumber
3
Saat menambahkan jawaban ke pertanyaan berusia sembilan tahun dengan lima jawaban yang ada, sangat penting untuk menunjukkan aspek baru yang unik dari pertanyaan yang dijawab oleh jawaban Anda.
Jason Aller
Jawaban yang saya posting sangat sederhana. Jadi berpikir seperti berbagi, yang dapat membantu pemula seperti saya.
user3520544