XPath để chọn nhiều thẻ


132

Cho định dạng dữ liệu đơn giản hóa này:

<a>
    <b>
        <c>C1</c>
        <d>D1</d>
        <e>E1</e>
        <f>don't select this one</f>
    </b>
    <b>
        <c>C2</c>
        <d>D2</d>
        <e>E1</e>
        <g>don't select me</g>
    </b>
    <c>not this one</c>
    <d>nor this one</d>
    <e>definitely not this one</e>
</a>

Làm thế nào bạn sẽ chọn tất cả các Cs, Ds và Es là con của Bcác phần tử?

Về cơ bản, một cái gì đó như:

a/b/(c|d|e)

Trong trường hợp của riêng tôi, thay vì chỉ a/b/, truy vấn dẫn đến việc lựa chọn những C, D, Enút thực sự là khá phức tạp vì vậy tôi muốn tránh làm điều này:

a/b/c|a/b/d|a/b/e

Điều này có thể không?

Câu trả lời:


207

Một câu trả lời đúng là :

/a/b/*[self::c or self::d or self::e]

Lưu ý rằng

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

là quá dài và không chính xác . Biểu thức XPath này sẽ chọn các nút như:

OhMy:c

NotWanted:d 

QuiteDifferent:e

2
'hoặc' không hoạt động trên từng cái, bạn sẽ cần sử dụng một đường thẳng đứng thay thế '|'
Guasqueño

8
@ Guasqueño, orlà một toán tử logic - nó hoạt động trên hai giá trị Boolean. Toán tử liên minh XPath |hoạt động trên hai bộ nút. Đây là khá khác nhau và có trường hợp sử dụng cụ thể cho từng người trong số họ. Việc sử dụng | có thể giải quyết vấn đề ban đầu, nhưng nó dẫn đến việc hiểu biểu thức XPath dài hơn và phức tạp hơn và khó khăn hơn. Biểu thức đơn giản hơn trong câu trả lời này, sử dụng ortoán tử tạo ra tập hợp nút mong muốn và có thể được chỉ định trong thuộc tính "select" của <xsl:for-each>thao tác XSLT. Hãy thử nó.
Dimitre Novatchev

4
@JonathanBenn, Bất cứ ai "không quan tâm đến không gian tên" thực sự không quan tâm đến XML và không sử dụng XML. Việc sử dụng local-name()chỉ đúng nếu chúng ta muốn chọn tất cả các thành phần có tên cục bộ đó, bất kể không gian tên của thành phần đó là gì. Đây là trường hợp rất hiếm - nói chung mọi người quan tâm đến sự khác biệt giữa: kitchen:tablesql:table, hoặc architecture:column, sql:column, array:column,military:column
Dimitre Novatchev

2
@DimitreNovatchev bạn làm cho một điểm tốt. Tôi đang sử dụng XPath để kiểm tra HTML, đây là trường hợp cạnh trong đó không gian tên không quá quan trọng ...
Jonathan Benn

2
Đó là siêu. Nơi mà bạn đã đưa ra điều đó?
Keith Tyler

46

Thay vào đó, bạn có thể tránh sự lặp lại với kiểm tra thuộc tính:

a/b/*[local-name()='c' or local-name()='d' or local-name()='e']

Trái ngược với ý kiến ​​đối kháng của Dimitre, ở trên không phảikhông chính xác trong trường hợp OP không chỉ định tương tác với các không gian tên. Các self::trục là namespace hạn chế, local-name()không phải là. Nếu ý định của OP là nắm bắt c|d|ebất kể không gian tên (mà tôi đề xuất thậm chí là một kịch bản có khả năng do bản chất HOẶC của vấn đề) thì đó là "một câu trả lời khác vẫn có một số phiếu tích cực" không chính xác.

Bạn không thể dứt khoát mà không có định nghĩa, mặc dù tôi khá vui khi xóa câu trả lời của tôi là thực sự không chính xác nếu OP làm rõ câu hỏi của anh ấy sao cho tôi không chính xác.


3
Nói với tư cách là bên thứ 3 ở đây - cá nhân, tôi thấy đề xuất của Dimitre là cách thực hành tốt hơn trừ trường hợp người dùng có lý do rõ ràng (và tốt) để quan tâm đến tên thẻ không liên quan đến không gian tên; nếu bất cứ ai làm điều này chống lại một tài liệu mà tôi đang trộn lẫn trong nội dung có tên khác nhau (có lẽ được dự định đọc bởi một chuỗi công cụ khác), tôi sẽ coi hành vi của họ là không phù hợp. Điều đó nói rằng, đối số là - như bạn đề xuất - một chút không thành công.
Charles Duffy

4
Chính xác những gì tôi đang tìm kiếm. Không gian tên XML theo cách chúng được sử dụng trong cuộc sống thực là một mớ hỗn độn. Đối với việc thiếu khả năng chỉ định một cái gì đó như / a / b / ( : c | : d | * e) giải pháp của bạn là chính xác những gì cần thiết. Những người theo chủ nghĩa thuần túy có thể tranh luận tất cả những gì họ muốn nhưng người dùng không quan tâm rằng ứng dụng bị hỏng vì bất cứ điều gì tạo ra tệp đầu vào của họ đều làm hỏng không gian tên. Họ chỉ muốn nó hoạt động.
Ghostrider

7
Tôi chỉ có ý tưởng mơ hồ về sự khác biệt giữa hai câu trả lời này và không ai bận tâm giải thích. "Không gian tên hạn chế" nghĩa là gì? Nếu tôi sử dụng local-name(), điều đó có nghĩa là nó sẽ khớp các thẻ với bất kỳ không gian tên nào? Nếu tôi sử dụng self::, không gian tên nào sẽ phải khớp? Làm thế nào tôi chỉ phù hợp OhMy:c?
meustrus

15

Tại sao không a/b/(c|d|e)? Tôi vừa thử với thư viện Saxon XML (được kết hợp độc đáo với một số tính tốt của Clojure) và có vẻ như nó hoạt động. abc.xmllà tài liệu được mô tả bởi OP.

(require '[saxon :as xml])
(def abc-doc (xml/compile-xml (slurp "abc.xml")))
(xml/query "a/b/(c|d|e)" abc-doc)
=> (#<XdmNode <c>C1</c>>
    #<XdmNode <d>D1</d>>
    #<XdmNode <e>E1</e>>
    #<XdmNode <c>C2</c>>
    #<XdmNode <d>D2</d>>
    #<XdmNode <e>E1</e>>)

8
Có, nhưng đó là XPath 2.0

Điều này làm việc tốt cho tôi. Có vẻ như XPath 2.0 là mặc định cho phân tích cú pháp HTML trong lxml trên Python 2.
Martin Burch

-1

Không chắc điều này có giúp ích gì không, nhưng với XSL, tôi sẽ làm một cái gì đó như:

<xsl:for-each select="a/b">
    <xsl:value-of select="c"/>
    <xsl:value-of select="d"/>
    <xsl:value-of select="e"/>
</xsl:for-each>

và XPath này sẽ không chọn tất cả con của các nút B:

a/b/*

Cảm ơn Calvin, nhưng tôi không sử dụng XSL và thực sự có nhiều yếu tố bên dưới B mà tôi không muốn chọn. Tôi sẽ cập nhật ví dụ của tôi để rõ ràng hơn.
nickf

Ồ, trong trường hợp đó annakata dường như có giải pháp.
Calvin
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.