2010-10-13 19 views
15

Tôi muốn xây dựng một truy vấn XPath sẽ trả về phần tử "div" hoặc "bảng", miễn là nó có hậu duệ chứa văn bản "abc". Một trong những báo trước là nó không thể có bất kỳ div hoặc bảng con cháu.Truy vấn XPath với hậu duệ và văn bản con cháu() predicates

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

Vì vậy, chỉ đúng kết quả của truy vấn này sẽ là:

/div/table/form/div 

nỗ lực tốt nhất của tôi trông giống như sau:

//div[contains(//text(), "abc") and not(descendant::div or descendant::table)] | //table[contains(//text(), "abc") and not(descendant::div or descendant::table)] 

nhưng không trả lại kết quả chính xác.

Cảm ơn sự giúp đỡ của bạn.

+0

Câu hỏi hay, +1. Xem câu trả lời của tôi cho những gì có lẽ là giải pháp ngắn nhất. :) –

Trả lời

32

Something khác nhau: :)

//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] 

vẻ ngắn hơn rất nhiều so với các giải pháp khác, phải không? :)

Translated để đơn giản bằng tiếng Anh: Đối với bất kỳ nút văn bản trong tài liệu nào có chứa chuỗi "abc" chọn tổ tiên đầu tiên của mình đó là hoặc là một div hoặc một table.

Đây là hiệu quả hơn, vì chỉ một quét toàn bộ cây tài liệu (và không phải bất kỳ khác) là bắt buộc, và ancestor::* traversal là rất rẻ so với một descendent:: (cây) quét.

Để xác minh rằng giải pháp này "thực sự hoạt động":

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] "/> 
</xsl:template> 
</xsl:stylesheet> 

khi chuyển đổi này được thực hiện trên tài liệu XML cung cấp:

<div> 
    <table> 
    <form> 
     <div> 
     <span> 
      <p>abcdefg</p> 
     </span> 
     </div> 
     <table> 
     <span> 
      <p>123456</p> 
     </span> 
     </table> 
    </form> 
    </table> 
</div> 

các truy nã, kết quả đúng là được sản xuất:

<div> 
    <span> 
     <p>abcdefg</p> 
    </span> 
</div> 

Lưu ý: Không cần thiết phải sử dụng XSLT - bất kỳ máy chủ XPath 1.0 nào - chẳng hạn như DOM, phải có cùng kết quả.

+1

cảm ơn bạn đã trả lời và cảm ơn bạn đã +1. Tôi thích sự nhỏ gọn của câu trả lời này, tuy nhiên tôi không thể làm cho nó hoạt động trong các bài kiểm tra của tôi. Hai câu trả lời khác cho câu hỏi này phù hợp với tôi. Có thể có lỗi đánh máy trong phản hồi của bạn không? Tôi không thể yêu cầu để hiểu tất cả.[1] làm gì? Một lần nữa, nếu bạn có bất kỳ thông tin chi tiết nào về lý do tại sao câu trả lời này không hiệu quả đối với tôi và những người khác làm, tôi đánh giá cao điều đó. Tôi sẽ +1 cho thời gian của bạn nhưng tôi mới vào trang web này và chưa có khả năng. Cảm ơn. – juan234

+0

@ juan234: Tôi đã thêm vào câu trả lời của tôi một số mã xác minh mà mọi người có thể chạy và xác minh tính chính xác của kết quả. Việc xác minh này cho thấy tính chính xác của biểu thức - có * không * lỗi đánh máy. Bạn có thể gặp sự cố do các lý do khác nhau: từ việc sử dụng công cụ XPath 1.0 không đầy đủ cho các vấn đề trong mã của bạn - để xác định lý do cần thiết để xem mã của bạn. '[1]' có nghĩa là nút đầu tiên của nodeset được chọn bởi một phần của biểu thức ngay bên phải của '[1]' - trong các trục ngược (chẳng hạn như 'tổ tiên ::' nó thực sự có nghĩa là nút cuối cùng theo thứ tự tài liệu). –

+0

Tôi bị thuyết phục :) – juan234

1

bạn có thể thử:

//div[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] | 
//table[ 
    descendant::text()[contains(., "abc")] 
    and not(descendant::div or descendant::table) 
] 

không giúp đỡ mà?

1
//*[self::div|self::table] 
    [descendant::text()[contains(.,"abc")]] 
    [not(descendant::div|descendant::table)] 

Vấn đề với contains(//text(), "abc") là các chức năng truyền các nút chọn lấy nút đầu tiên.