2010-08-06 36 views
23

Làm cách nào để chọn tất cả các bảng giữa bảng có id là header_completed và bảng đầu tiên sau header_completed có một căn giữa? Đây là html Tôi đang chọn nó từ:Chọn anh chị em giữa hai nút bằng cách sử dụng XPath

<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center" class="header_completed"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- these 5 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> <-- 
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
<table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 

Tôi đã thử sử dụng //table[@id="header_completed"]/following-sibling::node()[following-sibling::table[@align="center"][1]] nhưng nó không hoạt động.

+0

Câu hỏi hay (+1). Xem câu trả lời của tôi cho một lời giải thích và một giải pháp hoàn chỉnh. –

+0

Chết tiệt, bạn đã thực sự làm hết sức mình để hỏi một câu hỏi hay. Để có điểm đầy đủ về tính hữu ích và thiết kế mạnh mẽ, tôi rất muốn mở rộng nó thành 2 trường hợp của một '[@ class = 'header_completed']' ... một số nút ... '[@ align = 'center' ] 'và chụp 2 bộ đó trong 1 lần. – Wrikken

+0

+1 cho câu hỏi hay. –

Trả lời

24

Tôi tin rằng biểu thức XPath này chọn các nút bạn muốn:

//table[@class="header_completed"]/ 
    following-sibling::table[@align="center"][1]/ 
     preceding-sibling::table[ 
      preceding-sibling::table[@class="header_completed"] 
     ] 

Trước tiên, tôi điều hướng đến table với @class="header_completed".

Từ đó tôi chọn bảng anh chị em đầu tiên sau đây với @align="center".

Từ đó tôi chọn tất cả các bảng anh chị em trước có một anh chị em trước đó là bảng có @class="header_completed".

+3

+1 cho giải pháp khả thi thực tế không có vị trí mã hóa cứng, suy nghĩ tốt. Tuy nhiên, mở rộng trên nó, tôi đã tự hỏi liệu bạn có hiểu rõ về khả năng tôi đưa ra trong các nhận xét cho câu hỏi hay không: nếu chúng ta có 2 chuỗi giữa các nút hợp lệ, ví dụ: nếu chúng ta sao chép ví dụ sau chính nó, cách vẫn nhận được 10 nút kết quả (giữa 'match1_of_start' và' match1_of_end' + 'match2_of_start' &' match2_of_end') thay vì toàn bộ danh sách (giữa 'match1_of_start' &' match2_of_end'). – Wrikken

+0

+1 Một giải pháp tuyệt vời và một giải pháp dễ hiểu. Cảm ơn nhiều. – Alex

+0

@Wrikken Đó là một thách thức cho chắc chắn. Tôi sẽ phải suy nghĩ về điều đó. – mwittrock

25

Sử dụng phương pháp Kayessian của nút thiết lập giao:

Giao điểm của hai nút-bộ $ns1$ns2 đánh giá bởi các biểu thức XPath sau:

$ns1[count(.| $ns2)=count($ns2)] 

Nếu chúng ta có tài liệu XML sau:

<t> 
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center" class="header_completed"></table> 
    <table border="0" cellpadding="0" cellspacing="1" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="2" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="3" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="4" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="5" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920"></table> 
    <table border="0" cellpadding="0" cellspacing="0" width="920" align="center"></table> 
</t> 

sau đó theo các câu hỏi, chúng tôi có:

$ns1 là:

/*/*[@class='header_completed'][1] 
        /following-sibling::* 

$ns2 là:

/*/*[@class='header_completed'][1] 
      /following-sibling::*[@align='center'][1] 
        /preceding-sibling::* 

Chúng tôi chỉ đơn giản là thay thế $ns1$ns2 trong công thức Kayessian và nhận được biểu thức XPath sau, chọn chính xác 5 elemen mong muốn ts:

/*/*[@class='header_completed'][1] 
         /following-sibling::* 
       [count(.|/*/*[@class='header_completed'][1] 
          /following-sibling::*[@align='center'][1] 
           /preceding-sibling::*) 
       = 
       count(/*/*[@class='header_completed'][1] 
          /following-sibling::*[@align='center'][1] 
           /preceding-sibling::*) 
       ] 

Để xác minh rằng đây thực sự là giải pháp, chúng tôi sử dụng chuyển đổi XSLT này:

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

<xsl:variable name="ns1" select= 
     "/*/*[@class='header_completed'][1] 
         /following-sibling::* 
     "/> 
    <xsl:variable name="ns2" select= 
     "/*/*[@class='header_completed'][1] 
       /following-sibling::*[@align='center'][1] 
         /preceding-sibling::* 
     "/> 

    <xsl:template match="/"> 
     <xsl:copy-of select= 
     "$ns1[count(.| $ns2)=count($ns2)] 
     "/> 
     <DELIMITER/> 
     <xsl:copy-of select= 
     "/*/*[@class='header_completed'][1] 
         /following-sibling::* 
       [count(.|/*/*[@class='header_completed'][1] 
          /following-sibling::*[@align='center'][1] 
           /preceding-sibling::*) 
       = 
       count(/*/*[@class='header_completed'][1] 
          /following-sibling::*[@align='center'][1] 
           /preceding-sibling::*) 
       ] 
     "/> 
    </xsl:template> 
</xsl:stylesheet> 

Khi chuyển đổi này được áp dụng trên các tài liệu XML trên, kết quả chính xác muốn là được sản xuất:

<table border="0" cellpadding="0" cellspacing="1" width="920"/> 
<table border="0" cellpadding="0" cellspacing="2" width="920"/> 
<table border="0" cellpadding="0" cellspacing="3" width="920"/> 
<table border="0" cellpadding="0" cellspacing="4" width="920"/> 
<table border="0" cellpadding="0" cellspacing="5" width="920"/> 
<DELIMITER/> 
<table border="0" cellpadding="0" cellspacing="1" width="920"/> 
<table border="0" cellpadding="0" cellspacing="2" width="920"/> 
<table border="0" cellpadding="0" cellspacing="3" width="920"/> 
<table border="0" cellpadding="0" cellspacing="4" width="920"/> 
<table border="0" cellpadding="0" cellspacing="5" width="920"/> 

XPath 2.0 solu tion:

Trong XPath 2.0, chúng tôi có thể sử dụng toán tử intersect và các toán tử >> và/hoặc <<.

2.0 biểu thức XPath tương ứng với sử dụng trước đó biểu thức XPath 1.0 là:

 /*/*[ . 
     >> 
     /*/*[@class='header_completed'][1] 
     ] 

    intersect 

    /*/*[ /*/*[@class='header_completed'][1] 
       /following-sibling::*[@align='center'][1] 
      >> 
       . 
     ] 

Dưới đây là một giải pháp XSLT 2.0, chứng minh sự đúng đắn của XSLT này 2.0 biểu:

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

<xsl:variable name="ns1" select= 
    "/*/*[ . 
     >> 
     /*/*[@class='header_completed'][1] 
     ] 
    "/> 

    <xsl:variable name="ns2" select= 
     "/*/*[ /*/*[@class='header_completed'][1] 
       /following-sibling::*[@align='center'][1] 
      >> 
       . 
      ] 
     "/> 

<xsl:template match="/"> 
    <xsl:sequence select="$ns1 intersect $ns2"/> 
    <DELIMITER/> 
    <xsl:sequence select= 
    "/*/*[ . 
     >> 
     /*/*[@class='header_completed'][1] 
     ] 

    intersect 

    /*/*[ /*/*[@class='header_completed'][1] 
       /following-sibling::*[@align='center'][1] 
      >> 
       . 
     ] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

khi áp dụng trên tài liệu XML được xác định trước, chúng tôi một lần nữa nhận được cùng một mong muốn, kết quả chính xác:

<table border="0" cellpadding="0" cellspacing="1" width="920"/> 
<table border="0" cellpadding="0" cellspacing="2" width="920"/> 
<table border="0" cellpadding="0" cellspacing="3" width="920"/> 
<table border="0" cellpadding="0" cellspacing="4" width="920"/> 
<table border="0" cellpadding="0" cellspacing="5" width="920"/> 
<DELIMITER/> 
<table border="0" cellpadding="0" cellspacing="1" width="920"/> 
<table border="0" cellpadding="0" cellspacing="2" width="920"/> 
<table border="0" cellpadding="0" cellspacing="3" width="920"/> 
<table border="0" cellpadding="0" cellspacing="4" width="920"/> 
<table border="0" cellpadding="0" cellspacing="5" width="920"/> 
+0

+1 Giải pháp rất thanh lịch. Một vấn đề nhỏ: bạn đã mã hóa cứng việc sử dụng phần tử * third * với '@ align = 'center'', trong khi OP chỉ ra phần tử * đầu tiên sau *' @ class =' ​​header_completed'' có '@ align = 'center''. –

+0

@ Niels-van-der-Rest: Vâng, cảm ơn vì đã chú ý đến điều này. Tôi sẽ cập nhật câu trả lời của tôi sau ngày hôm nay. –

+0

+1 Câu trả lời rất sâu sắc và câu trả lời rất hữu ích. Nó có những giải thích và chứng minh tuyệt vời. Cảm ơn bạn. – Alex

Các vấn đề liên quan