2008-10-22 29 views
13

Tôi đã tìm thấy this page mô tả phương pháp Muenchian, nhưng tôi nghĩ rằng tôi đang áp dụng sai.Cách chọn các nút duy nhất

Hãy xem xét rằng điều này sẽ trả về một tập hợp các lứa tuổi:

/doc/class/person/descriptive[(@name='age')]/value 

1..2..2..2..3..3..4..7

Nhưng tôi muốn một nodeset chỉ có một nút cho mỗi độ tuổi.

1..2..3..4..7

Mỗi dường như trả lại tất cả các giá trị, thay vì giá trị duy nhất:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::value)]/value 
/doc/class/person/descriptive[(@name='age')]/value[not(value=preceding-sibling::value)] 

gì am Tôi bị mất?

+0

Rất tiếc, dường như ví dụ tôi tiếp theo là * không * phương pháp Muenchian - thay vào đó tác giả đã phản đối điều gì. – pc1oad1etter

Trả lời

20

Dưới đây là một ví dụ:

<root> 
    <item type='test'>A</item> 
    <item type='test'>B</item> 
    <item type='test'>C</item> 
    <item type='test'>A</item> 
    <item type='other'>A</item> 
    <item type='test'>B</item> 
    <item type='other'>D</item> 
    <item type=''>A</item> 
</root> 

Và XPath:

//preceding::item/preceding::item[not(.=preceding-sibling::item)]/text() 

Kết quả: ABCD

EDIT: Vì mousio đã nhận xét điều này không ghi lại mục cuối cùng trong danh sách nếu đó là lần duy nhất nó xuất hiện. Lấy đó và bình luận Feanor của vào tài khoản, đây là một giải pháp tốt hơn:

/root/item[not(.=preceding-sibling::item)] 
+0

Rõ ràng, bạn có thể sử dụng XPath bổ sung để hạn chế dựa trên thuộc tính type hoặc dữ liệu khác trong tệp thực của bạn. Tôi chỉ có nó trong đó trong bài kiểm tra nhanh của tôi. –

+0

Cũng lưu ý rằng "mục" trong XPath không phải là một từ khóa, nó là tên của phần tử trong tài liệu XML mà trước :: và trước-anh chị em :: trục đang làm việc trên. –

+1

Tôi không nghĩ rằng việc sử dụng // là hữu ích ở đây vì cấu trúc được cho nên chúng ta biết tất cả các nút mục và chỉ các nút mục, xuất hiện dưới nút gốc, điều này tốt hơn: root/item [. ! = trước-anh chị em] – markmnl

1

Bạn không thiếu tham chiếu đến 'mô tả' ngay sau giá trị trước đó? Một số điều như sau:

/doc/class/person/descriptive[(@name='age')][not(value=preceding-sibling::descriptive[@name='age']/value)]/value 

(đã không kiểm tra nó)

14

Dưới đây là phiên bản Muenchian của câu trả lời BQ bằng cách sử dụng dữ liệu của mình:

<?xml version="1.0"?> 
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:output indent="yes" method="text"/> 
    <xsl:key name="item-by-value" match="item" use="."/> 

    <xsl:template match="/"> 
    <xsl:apply-templates select="/root/item"/> 
    </xsl:template> 

    <xsl:template match="item"> 
    <xsl:if test="generate-id() = generate-id(key('item-by-value', normalize-space(.)))"> 
     <xsl:value-of select="."/> 
     <xsl:text> 
</xsl:text> 
    </xsl:if> 
    </xsl:template> 

    <xsl:template match="text()"> 
    <xsl:apply-templates/> 
    </xsl:template> 
</xsl:stylesheet> 

này chuyển cho

Một
B
C
D

  1. Tra cứu key() ở trên trong mẫu cho item trả về một nút có chứa tất cả các phần tử item có cùng giá trị chuỗi như nút ngữ cảnh.
  2. Nếu bạn áp dụng một hàm dự kiến ​​một nút duy nhất cho một nút, nó sẽ hoạt động trên nút đầu tiên trong nút đó.
  3. Tất cả các cuộc gọi đến generate-id() được đảm bảo tạo cùng một ID cho một nút nhất định trong một lần truyền qua một tài liệu.
  4. Do đó, kiểm tra sẽ đúng nếu nút ngữ cảnh là nút giống với nút đầu tiên được trả về bởi lệnh gọi key().
+0

với một chút chỉnh sửa của các trận đấu và sử dụng các tham số này làm việc như một say mê bên trong một phần tử cho mỗi; cảm ơn! –

+0

@ChuckB nếu phần tử mặt hàng có giá trị chứa hai khoảng trắng không được chọn (ví dụ: A B). Bất kỳ ý tưởng tại sao là điều đó? –

+0

'position()' hoạt động như thế nào trong ví dụ này? Giả sử bạn bắt đầu với một nodeset của 'A, A, A, B, B, B' và đặt nó xuống 'A, B', sẽ có cách để có được' vị trí() 'mới của' A' và 'B', phải là' 1' cho 'A' và' 2' cho 'B'? – NessDan

2

Phương pháp Muenchian sử dụng các phím để tạo danh sách các mục duy nhất từ ​​tập hợp nút. Đối với dữ liệu của bạn, chìa khóa sẽ trông như thế này:

<!-- Set the name to whatever you want --> 
<xsl:key name="PeopleAges" match="/doc/class/person/descriptive[@name = 'age']/value" use="." /> 

Từ đó, cá nhân tôi sẽ sử dụng xsl:apply-templates nhưng bạn có thể sử dụng sau đây select thuộc tính ở những nơi khác:

<!-- you can change `apply-templates` to: `copy-of` or `for-each`. --> 
<xsl:apply-templates select="/doc/class/person/descriptive[@name = 'age']/value[count(. | key('PeopleAges', .)[1]) = 1]" /> 

Trận đấu kèm theo cho một trên đây là đơn giản hơn nhiều:

<xsl:template match="person/descriptive[@name = 'age']/value"> 
    <strong>Age: </strong><xsl:value-of select="." /> 
</xsl:template> 
3

đối với những người vẫn tìm kiếm một lựa chọn khác biệt trong XSLT:

Với XSLT 2.0, bạn có thể sử dụng "riêng biệt-giá trị (/ doc/lớp/người/mô tả [(@ name = 'tuổi')]/giá trị)"

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