XPath chứa (text (), 'some string') không hoạt động khi được sử dụng với nút có nhiều hơn một nút con Văn bản


259

Tôi có một vấn đề nhỏ với Xpath chứa với dom4j ...

Hãy nói rằng XML của tôi là

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

Hãy nói rằng tôi muốn tìm tất cả các nút có ABC trong văn bản được cung cấp Phần tử gốc ...

Vì vậy, xpath mà tôi cần viết sẽ là

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

Tuy nhiên đây không phải là những gì Dom4j trả về .... đây có phải là vấn đề của dom4j hay sự hiểu biết của tôi về cách xpath hoạt động. vì truy vấn đó chỉ trả về phần tử Street và không phải phần tử Comment.

DOM làm cho phần tử Comment thành phần tử tổng hợp có bốn thẻ hai

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

Tôi sẽ giả sử rằng truy vấn vẫn trả về phần tử vì nó sẽ tìm phần tử và chạy chứa nó nhưng nó không ... ...

truy vấn sau đây trả về phần tử nhưng nó trả về xa hơn phần tử, nó cũng trả về phần tử cha ... điều không mong muốn đối với vấn đề ...

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

Có ai biết truy vấn xpath sẽ chỉ trả về các Element <Street/><Comment/>không?


Theo như tôi có thể nói, //*[contains(text(),'ABC')]chỉ trả về <Street>phần tử. Nó không trả lại bất kỳ tổ tiên của <Street>hoặc <Comment>.
Ken Bloom

Câu trả lời:


707

Các <Comment>thẻ chứa hai nút văn bản và hai<br> nút như trẻ em.

Biểu hiện xpath của bạn là

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

Để phá vỡ điều này,

  1. * là một bộ chọn phù hợp với bất kỳ phần tử nào (tức là thẻ) - nó trả về một tập hợp nút.
  2. Đây []là một điều kiện hoạt động trên mỗi nút riêng lẻ trong tập nút đó. Nó phù hợp nếu bất kỳ nút riêng lẻ nào nó hoạt động phù hợp với các điều kiện bên trong dấu ngoặc.
  3. text()là một bộ chọn phù hợp với tất cả các nút văn bản là con của nút bối cảnh - nó trả về một tập hợp nút.
  4. containslà một hàm hoạt động trên một chuỗi. Nếu nó được thông qua một tập hợp nút, tập hợp nút được chuyển đổi thành một chuỗi bằng cách trả về giá trị chuỗi của nút trong tập hợp nút đầu tiên theo thứ tự tài liệu . Do đó, nó chỉ có thể khớp với nút văn bản đầu tiên trong <Comment>phần tử của bạn - cụ thể là BLAH BLAH BLAH. Vì điều đó không phù hợp, bạn không nhận được <Comment>kết quả của mình.

Bạn cần thay đổi điều này thành

//*[text()[contains(.,'ABC')]]
  1. * là một bộ chọn phù hợp với bất kỳ phần tử nào (tức là thẻ) - nó trả về một tập hợp nút.
  2. Bên ngoài []là một điều kiện hoạt động trên từng nút riêng lẻ trong tập nút đó - ở đây nó hoạt động trên từng phần tử trong tài liệu.
  3. text()là một bộ chọn phù hợp với tất cả các nút văn bản là con của nút bối cảnh - nó trả về một tập hợp nút.
  4. Bên trong []là một điều kiện hoạt động trên mỗi nút trong tập nút đó - ở đây mỗi nút văn bản riêng lẻ. Mỗi nút văn bản riêng lẻ là điểm bắt đầu cho bất kỳ đường dẫn nào trong ngoặc và cũng có thể được gọi một cách rõ ràng như .trong ngoặc. Nó phù hợp nếu bất kỳ nút riêng lẻ nào nó hoạt động phù hợp với các điều kiện bên trong dấu ngoặc.
  5. containslà một hàm hoạt động trên một chuỗi. Ở đây nó được thông qua một nút văn bản cá nhân ( .). Vì nó được thông qua nút văn bản thứ hai trong <Comment>thẻ riêng lẻ, nó sẽ thấy 'ABC'chuỗi và có thể khớp với nó.

1
Tuyệt vời tôi là một chút của một xpath noob, vì vậy hãy để tôi lấy cái này, text () là một hàm có biểu thức chứa (., 'ABC'), có cơ hội nào bạn có thể giải thích không vì vậy tôi không làm điều này đồ ngu ngốc một lần nữa;)
Mike Milkin

28
Tôi đã chỉnh sửa câu trả lời của mình để đưa ra lời giải thích dài. Tôi thực sự không biết nhiều về XPath - Tôi chỉ thử nghiệm một chút cho đến khi tôi vấp phải sự kết hợp đó. Khi tôi có một kết hợp làm việc, tôi đã đoán được điều gì đang xảy ra và xem xét tiêu chuẩn XPath để xác nhận những gì tôi nghĩ đang diễn ra và viết lời giải thích.
Ken Bloom

2
Làm thế nào bạn sẽ làm cho điều này một trường hợp tìm kiếm không nhạy cảm?
Zack

@Zack: Vui lòng đặt câu hỏi mới này.
dùng1129682

1
Tôi biết đây là một chủ đề cũ, nhưng bất cứ ai cũng có thể nhận xét nếu có một sự khác biệt cơ bản, tốt nhất là với một số trường hợp thử nghiệm đơn giản giữa câu trả lời được đưa ra bởi Ken Bloom và //*[contains(., 'ABC')]. Tôi đã luôn sử dụng mô hình do Mike Milkin đưa ra, nghĩ rằng nó phù hợp hơn, nhưng chỉ thực containshiện trong bối cảnh hiện tại dường như thực sự là điều tôi muốn thường xuyên hơn.
knickum

7

[contains(text(),'')]chỉ trả về đúng hay sai. Nó sẽ không trả về bất kỳ kết quả yếu tố nào.


điều này sẽ không hoạt động nếu tôi có '' hoặc '' làm thế nào chúng ta có thể cắt?
chia sẻ

contains(text(),'JB-')không hoạt động conatainslấy hai chuỗi làm đối số - contains(**string**, **string**)! text () không phải là chuỗi , là một hàm!
AtachiShadow

6

Tài liệu XML:

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

Biểu thức XPath:

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

//*phù hợp với bất kỳ yếu tố hậu duệ của nút gốc . Đó là, bất kỳ phần tử nào ngoài nút gốc.

[...]là một vị ngữ , nó lọc tập hợp nút. Nó trả về các nút đó ...true:

Một vị từ lọc một tập hợp nút [...] để tạo một tập hợp nút mới. Đối với mỗi nút trong tập hợp nút được lọc, PredicateExpr được ước tính [...]; nếu PredicateExpr đánh giá là đúng cho nút đó, thì nút được bao gồm trong tập hợp nút mới; mặt khác, nó không được bao gồm.

contains('haystack', 'needle')trả về truenếu haystack chứa needle :

Hàm: boolean chứa (chuỗi, chuỗi)

Hàm chứa trả về true nếu chuỗi đối số thứ nhất chứa chuỗi đối số thứ hai và ngược lại trả về false.

Nhưng contains()lấy một chuỗi làm tham số đầu tiên của nó. Và nó đã thông qua các nút. Để giải quyết vấn đề đó, mọi nút hoặc tập hợp nút được truyền khi tham số đầu tiên được chuyển đổi thành chuỗi bởi string()hàm:

Một đối số được chuyển đổi thành kiểu chuỗi như thể bằng cách gọi hàm chuỗi.

string()Hàm trả về string-valuecủa nút đầu tiên :

Một tập hợp nút được chuyển đổi thành một chuỗi bằng cách trả về giá trị chuỗi của nút trong tập hợp nút đầu tiên theo thứ tự tài liệu. Nếu tập hợp nút trống, một chuỗi trống được trả về.

string-valuecủa một nút phần tử :

Giá trị chuỗi của một nút phần tử là nối các giá trị chuỗi của tất cả các hậu duệ nút văn bản của nút phần tử theo thứ tự tài liệu.

string-valuecủa một nút văn bản :

Giá trị chuỗi của nút văn bản là dữ liệu ký tự.

Vì vậy, về cơ bản string-valuelà tất cả các văn bản được chứa trong một nút (nối của tất cả các nút văn bản con cháu).

text() là một bài kiểm tra nút phù hợp với bất kỳ nút văn bản nào:

Văn bản kiểm tra nút () đúng với bất kỳ nút văn bản nào. Ví dụ: child :: text () sẽ chọn nút con văn bản của nút bối cảnh.

Như đã nói, //*[contains(text(), 'ABC')]khớp với bất kỳ phần tử nào (trừ nút gốc), nút văn bản đầu tiên chứa ABC. Vì text()trả về một tập hợp nút chứa tất cả các nút văn bản con của nút bối cảnh (liên quan đến biểu thức được ước tính). Nhưng contains()chỉ mất cái đầu tiên. Vì vậy, đối với tài liệu trên đường dẫn khớp vớiStreet phần tử.

Biểu thức sau //*[text()[contains(., 'ABC')]]phù hợp với bất kỳ phần tử nào (nhưng nút gốc), có ít nhất một nút văn bản con, có chứa ABC. .đại diện cho nút bối cảnh. Trong trường hợp này, đó là nút văn bản con của bất kỳ phần tử nào ngoại trừ nút gốc. Vì vậy, đối với tài liệu trên đường dẫn khớp với Streetvà các Commentyếu tố.

Bây giờ, sau đó, //*[contains(., 'ABC')]khớp với bất kỳ phần tử nào (nhưng nút gốc) có chứa ABC(trong phần nối của các nút văn bản con cháu). Đối với tài liệu ở trên, nó khớp với các Homephần tử Addr, các Street, và các Commentphần tử. Như vậy, //*[contains(., 'BLAH ABC')]phù hợp với Home, các Addrvà các Commentyếu tố.


0

Phải mất một lúc nhưng cuối cùng tôi cũng tìm ra. Xpath tùy chỉnh có chứa một số văn bản dưới đây hoạt động hoàn hảo cho tôi.

//a[contains(text(),'JB-')]

2
contains(text(),'JB-')không hoạt động conatainslấy hai chuỗi làm đối số - contains(**string**, **string**)! text () không phải là chuỗi , là một hàm!
AtachiShadow

0

Câu trả lời được chấp nhận sẽ trả về tất cả các nút cha. Để chỉ nhận các nút thực tế với ABC ngay cả khi chuỗi nằm sau
:

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

0
//*[text()='ABC'] 

trả lại

<street>ABC</street>
<comment>BLAH BLAH BLAH <br><br>ABC</comment>

3
Khi thêm câu trả lời cho câu hỏi chín năm tuổi với năm câu trả lời hiện có, điều rất quan trọng là chỉ ra khía cạnh mới độc đáo nào của câu hỏi mà câu trả lời của bạn giải quyết.
Jason Aller

Trả lời tôi đăng rất đơn giản. Vì vậy, suy nghĩ như chia sẻ, có thể giúp những người mới bắt đầu như tôi.
dùng3520544
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.