2012-01-26 27 views
6

Tôi có một đoạn XML khủng khiếp mà tôi cần xử lý thông qua BizTalk và tôi đã quản lý để bình thường hóa nó vào ví dụ dưới đây. Tôi không có ninja XSLT, nhưng giữa web và trình gỡ lỗi VS2010, tôi có thể tìm đường đi xung quanh XSL.Cần biến đổi XSLT để loại bỏ các phần tử trùng lặp - được sắp xếp theo thuộc tính

Bây giờ tôi cần một chút thông minh của XSLT để "loại bỏ" các phần tử trùng lặp và chỉ giữ các phần tử mới nhất, theo quyết định của ngày trong thuộc tính ValidFromDate.

Thuộc tính ValidFromDate là của XSD: Loại ngày.

<SomeData> 
    <A ValidFromDate="2011-12-01">A_1</A> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B CalidFromDate="2011-12-03">B_1</B> 
    <B ValidFromDate="2012-01-17">B_2</B> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
    <C ValidFromDate="2011-01-20">C_2</C> 
</SomeData> 

Sau một biến đổi tôi muốn chỉ giữ những dòng này:

<SomeData> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
</SomeData> 

Bất kỳ manh mối như thế nào tôi đặt rằng XSL với nhau? Tôi đã làm trống internet cố gắng tìm một giải pháp, và tôi đã thử rất nhiều kịch bản phân loại XSL thông minh, nhưng không có gì tôi cảm thấy đưa tôi đi đúng hướng.

+0

Ngoài ra ... vì điều này sẽ được gọi từ bản đồ BizTalk, và do đó. NET Tôi bị giới hạn ở XSLT 1.0 ... – LarsWA

+2

Có thể 'C_1' thay vì' C_2'? –

+0

Có khóa học ... cảm ơn. Chỉnh sửa điều này trong nhiệm vụ của tôi. – LarsWA

Trả lời

2

Một giải pháp XSLT 1.0 hơi đơn giản và ngắn hơn so với @lwburk:

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

<xsl:key name="kName" match="*/*" use="name()"/> 

<xsl:template match="/"> 
    <xsl:apply-templates select= 
    "*/*[generate-id() 
     = 
     generate-id(key('kName', name())[1]) 
     ] 
    "/> 
</xsl:template> 

<xsl:template match="*/*"> 
    <xsl:for-each select="key('kName', name())"> 
    <xsl:sort select="@ValidFromDate" order="descending"/> 
    <xsl:if test="position() = 1"> 
    <xsl:copy-of select="."/> 
    </xsl:if> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

khi chuyển đổi này được áp dụng trên các tài liệu XML cung cấp:

<SomeData> 
    <A ValidFromDate="2011-12-01">A_1</A> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B CalidFromDate="2011-12-03">B_1</B> 
    <B ValidFromDate="2012-01-17">B_2</B> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
    <C ValidFromDate="2011-01-20">C_2</C> 
</SomeData> 

mong muốn, kết quả chính xác được tạo ra:

<A ValidFromDate="2012-01-19">A_2</A> 
<B ValidFromDate="2012-01-19">B_3</B> 
<C ValidFromDate="2012-01-20">C_1</C> 
3

Giải pháp tối ưu cho vấn đề này với Xslt 1.0 sẽ là sử dụng nhóm Muenchian. (Cho rằng các yếu tố đã được sắp xếp bởi thuộc tính ValidFromDate) stylesheet sau nên làm như lừa:

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="xml" indent="yes"/> 

    <xsl:key name="element-key" match="/SomeData/*" use="name()" /> 

    <xsl:template match="/SomeData"> 
    <xsl:copy> 
     <xsl:for-each select="*[generate-id() = generate-id(key('element-key', name()))]"> 
     <xsl:copy-of select="(. | following-sibling::*[name() = name(current())])[last()]" /> 
     </xsl:for-each> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Đây là kết quả tôi nhận được khi chạy nó chống lại Xml mẫu của bạn:

<?xml version="1.0" encoding="utf-8"?> 
<SomeData> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2011-01-20">C_2</C> 
</SomeData> 
+0

Xin chào, tôi đã sửa đổi câu trả lời của bạn. Cả hai sản xuất cùng một đầu ra cho nguồn XML của OP, nhưng tôi không thể biết liệu có những khác biệt nhỏ hơn hay không. –

+0

Không nên có bất kỳ sự khác biệt nào. Phiên bản của bạn sạch hơn nhiều! Cảm ơn! – Pawel

+1

Thật không may, cả hai đều sai :) –

1

Dựa trên Pawel's answer, tôi đã thay đổi sau đây, trong đó sản xuất cùng một kết quả:

<xsl:template match="/SomeData"> 
    <xsl:copy> 
    <xsl:copy-of select="*[generate-id() = generate-id(key('element-key', name())[last()])]"/> 
    </xsl:copy> 
</xsl:template> 

Nếu họ sản xuất cùng một kết quả mỗi lần, tôi thích điều này bởi vì nó sạch hơn một chút.

2

Các kiểu sau đây tạo ra kết quả chính xác mà không cần bất kỳ sự phụ thuộc vào thứ tự đầu vào:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> 
    <xsl:output omit-xml-declaration="yes" indent="yes"/> 
    <xsl:key name="byName" match="/SomeData/*" use="name()"/> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="SomeData"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:for-each select="*[generate-id()= 
            generate-id(key('byName', name())[1])]"> 
       <xsl:apply-templates select="key('byName', name())" mode="out"> 
        <xsl:sort select="translate(@ValidFromDate, '-', '')" 
           data-type="number" order="descending"/> 
       </xsl:apply-templates> 
      </xsl:for-each> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="SomeData/*" mode="out"> 
     <xsl:if test="position()=1"> 
      <xsl:apply-templates select="."/> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Output:

<SomeData> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
</SomeData> 

Lưu ý rằng kết quả là hơi khác so với những gì bạn được liệt kê như là sản lượng mong muốn, bởi vì C_1 thực sự là phần tử C mới nhất (tức là đầu vào không phải là đã được sắp xếp). Bằng cách dựa vào thứ tự sắp xếp ban đầu (và mù quáng theo kết quả mong đợi được liệt kê), các câu trả lời hiện tại thực sự không chính xác.

Giải thích:

  • Một xsl:key nhóm tất cả /SomeData/* bởi name()
  • Các bên ngoài for-each chọn mục đầu tiên trong mỗi nhóm
  • Templates sau đó được áp dụng cho tất cả các thành viên của nhóm đó, đó là được sắp xếp theo @ValidFromDate
  • Một mẫu bổ sung duy nhất xử lý việc chọn phần tử đầu tiên ra khỏi mỗi nhóm được sắp xếp
  • Một nhận dạng chuyển đổi mẫu chăm sóc phần còn lại
+0

Xin chào lwburk ... cảm ơn vì đã chỉ ra điều C_1. Tôi xấu. Mặc dù giải pháp đã hoạt động tốt. Tôi đã đi với giải pháp của Dimitre vì nó là một chút ngắn hơn và dễ dàng hơn để tổng quan và do đó duy trì cho những người sẽ phải làm bảo trì về điều này. – LarsWA

2

Dựa trên @ValidFromDate theo thứ tự:

XSLT:

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

    <xsl:key name="k" match="*" use="name()"/> 

    <xsl:template match="SomeData"> 
    <xsl:copy> 
     <xsl:apply-templates select="*[generate-id() = 
          generate-id(key('k', name()))]"/> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*"> 
    <xsl:apply-templates select="key('k', name())" mode="a"> 
     <xsl:sort select="@ValidFromDate" order="descending"/> 
    </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="*" mode="a"> 
    <xsl:if test="position() = 1"> 
     <xsl:copy-of select="."/> 
    </xsl:if> 
    </xsl:template> 

</xsl:stylesheet> 

áp dụng trên:

<SomeData> 
    <A ValidFromDate="2011-12-01">A_1</A> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B CalidFromDate="2011-12-03">B_1</B> 
    <B ValidFromDate="2012-01-17">B_2</B> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
    <C ValidFromDate="2011-01-20">C_2</C> 
</SomeData> 

sản xuất:

<SomeData> 
    <A ValidFromDate="2012-01-19">A_2</A> 
    <B ValidFromDate="2012-01-19">B_3</B> 
    <C ValidFromDate="2012-01-20">C_1</C> 
</SomeData> 
1

Giải pháp XLST 2.0 mà không dựa vào thứ tự đầu vào.

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions"> 
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 
    <xsl:template match="/"> 
     <SomeData> 
      <xsl:for-each-group select="/SomeData/*" group-by="name()"> 
        <xsl:for-each select="current-group()"> 
         <xsl:sort select="number(substring(attribute(),1,4))" order="descending" data-type="number"/> <!-- year--> 
         <xsl:sort select="number(substring(attribute(),6,2))" order="descending" data-type="number"/> <!-- month--> 
         <xsl:sort select="number(substring(attribute(),9,2))" order="descending" data-type="number"/> <!-- date--> 
         <xsl:if test="position()=1"> 
           <xsl:sequence select="."/> 
         </xsl:if> 
        </xsl:for-each> 
      </xsl:for-each-group> 
     </SomeData> 
</xsl:template> 
</xsl:stylesheet> 
+0

Yup .. XSLT 2.0 làm cho nó một chút đơn giản để làm điều này, nhưng Microsoft đã không nhận được xung quanh để thực hiện điều đó được nêu ra ... Cảm ơn mặc dù. – LarsWA

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