2009-11-20 3 views
21

Tôi muốn tính toán sự khác biệt giữa hai tệp XML hoặc các nút bằng cách sử dụng XSL/XSLT. Có bất kỳ bản định kiểu nào có sẵn hay bất kỳ cách làm đơn giản nào không?XML Diff: Làm thế nào để tạo ra XML diff bằng cách sử dụng XSLT?

+3

+1 Chỉ cần cho tham vọng của nó, BTW phiên bản XSLT là bạn tìm kiếm này để làm việc trong – AnthonyWJones

+0

Mục tiêu sẽ được diff hai nút trong cùng một tệp XML, có khả thi hơn không? – Vincent

+0

Bạn phải xác định các phương tiện bình đẳng và khác nhau về các thuật ngữ XML. Để xem xét tham khảo câu hỏi này http://stackoverflow.com/questions/4546190/compare-two-xml-files-with-xslt –

Trả lời

6

Câu hỏi thú vị! Tôi đã từng cố gắng làm một cái gì đó tương tự liên quan đến hai nguồn XML, và kinh nghiệm của tôi là không có cách nào.

Bạn có thể sử dụng cơ sở của XSL để bao gồm các chức năng do người dùng tạo và mã hóa thứ gì đó thực sự mượt mà. Nhưng tôi thực sự không thể nhìn thấy nó.

Nếu tôi làm điều gì đó như thế này, tôi sẽ xử lý hai tệp XML song song bằng DOM4J, cho phép tôi dễ dàng duyệt mã theo lập trình và thực hiện các truy vấn phụ chi tiết.

Cố gắng thực hiện điều này trong XSLT hoặc là chứng minh bạn là thiên tài hoặc khiến bạn trở nên điên loạn.

2

XSLT được định hướng dữ liệu, có nghĩa là, nó đi qua tệp XML nguồn duy nhất trên cùng để tìm kiếm các kết quả khớp mẫu trong biểu định kiểu XSL. Các mẫu không thực sự biết chúng ở đâu trong dữ liệu, chúng chỉ chạy mã của chúng khi được khớp. Bạn có thể tham khảo một nguồn XML khác, nhưng chương trình sẽ chạy theo traversal của nguồn gốc.

Vì vậy, khi bạn đến phần tử con thứ n của <blarg>, ví dụ: bạn có thể tra cứu đứa con thứ n của <blarg> trong một XML thứ hai bằng cách sử dụng hàm document(). Nhưng tính hữu ích của điều này phụ thuộc vào cấu trúc của XML của bạn và những gì so sánh bạn đang cố gắng làm.

Hành vi này trái ngược với hầu hết các tập lệnh truyền thống, chạy qua mã chương trình từ trên xuống dưới, gọi đến tệp dữ liệu khi được hướng dẫn. Quá trình xử lý sau - là thứ bạn có thể cần so sánh hai nguồn XML. XSLT sẽ bị hỏng khi so sánh ngay khi có sự khác biệt.

1

Có nhiều cách để làm điều này, nhưng tôi sẽ không nói nó đơn giản.

Trong quá khứ tôi đã sử dụng một tiện ích mã nguồn mở gọi diffmk, điều này tạo ra một XML đầu ra với thêm thẻ hiển thị những gì đã được thêm/gỡ bỏ ...

tôi đã phải viết một stylesheet thêm để sau đó chuyển đổi này vào một báo cáo HTML dễ đọc hơn.

Một số công cụ khác biệt như XMLSpy Diff dog là tốt, nhưng tốn kém.

1

Đây không phải là một bí ẩn! Dưới đây là các bước chung:

  1. @carillonator đúng về cách XSLT xử lý tài liệu. Vì vậy, để giúp chúng tôi dễ dàng kết hợp hai phiên bản tài liệu của bạn thành một tài liệu duy nhất bạn có thể sử dụng để chạy XSLT khác biệt (Bạn có thể thực hiện điều này thông qua dòng lệnh với bash hoặc bằng bất kỳ ngôn ngữ lập trình nào bạn đang sử dụng hoặc XSLT biến đổi [ống]). Nó chỉ là một đóng gói:

    <diff_container> 
        <version1> 
         ... first version here 
        </version1> 
        <version2> 
         ... second version here 
        </version2> 
    </diff_container> 
    
  2. Chúng tôi sau đó chạy tài liệu này thông qua diff XSLT của chúng tôi, XSLT sau đó có được công việc chỉ đơn giản là đi qua cây và so sánh các nút giữa hai phiên bản. Điều này có thể đi từ rất đơn giản (Là một yếu tố thay đổi? Di chuyển? Gỡ bỏ?) Để bán phức tạp. Một sự hiểu biết tốt về XPath làm cho điều này khá đơn giản.

    Giống như một số đã nói trước đây, bạn làm việc bên trong một môi trường khác để bạn bị giới hạn so với các công cụ như Diff Dog. Tuy nhiên, lợi ích của việc có thuật toán trong XSLT cũng có thể có giá trị thực.

Hy vọng điều này sẽ hữu ích. Chúc mừng!

2

Nếu những gì bạn có nghĩa là bằng cách khác là một cái gì đó giống như kiểm tra xem mục tồn tại trong một tài liệu (hoặc nút) nhưng không khác, bạn có thể sử dụng phím xpath() chức năng với một tham số thứ ba

<?xml version="1.0"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs ="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xsl xs"> 

<xsl:param name="doc2diff" required="yes"/> 
<!-- docB is root node of the "second" document --> 
<xsl:variable name="docB" select="document($doc2diff)"/> 
<!-- docA is the root node of the first document --> 
<xsl:variable name="docA" select="/"/> 
<xsl:output method="xml" encoding="UTF-8" indent="yes"/> 
<xsl:key name="items" match="Item" use="someId"/> 

<xsl:template match="/"> 
<ListOfItems> 
    <In_A_NotIn_B> 
    <xsl:apply-templates select="Item"> 
    <xsl:with-param name="otherDocument" select="$docB"/> 
    </xsl:apply-templates> 
    </In_A_NotIn_B> 
    <In_B_NotIn_A> 
    <xsl:apply-templates select="Item"> 
    <xsl:with-param name="otherDocument" select="$docA"/> 
    </xsl:apply-templates> 
    </In_B_NotIn_A> 
</ListOfItems> 
</xsl:template> 

<xsl:template match="Item"> 
<xsl:param name="otherDocument"/> 
    <xsl:variable name="SOMEID" select="someId"/> 
    <xsl:if test="empty(key('items', $SOMEID, $otherDocument))"> 
    <xsl:copy-of select="."/> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet>` 
2

Đây là stylesheet tôi đã viết để so sánh hai tệp XML với thứ tự khác nhau trong các nút và thuộc tính, nó sẽ tạo ra hai tệp văn bản chứa danh sách có thứ tự tất cả các nút của nút lá. Sử dụng bất kỳ công cụ so sánh văn bản nào để phát hiện ra sự khác biệt hoặc nâng cao XSLT để làm những gì bạn muốn.

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

<xsl:output method="text" indent="no" omit-xml-declaration="yes" name="output" /> 

<xsl:param name="OTHERFILENAME">xml_file_to_diff.xml</xsl:param> 
<xsl:param name="ORIGINAL_OUTPUT_FILENAME">ORIGINAL.txt</xsl:param> 
<xsl:param name="OTHER_OUTPUT_FILENAME">OTHER.txt</xsl:param> 

<xsl:template match="/"> 
    <xsl:call-template name="convertXMLHierarchyToFullPath"> 
     <xsl:with-param name="node" select="*"/> 
     <xsl:with-param name="filename" select="$ORIGINAL_OUTPUT_FILENAME"/> 
    </xsl:call-template> 
    <xsl:call-template name="convertXMLHierarchyToFullPath"> 
     <xsl:with-param name="node" select="document($OTHERFILENAME)/*"/> 
     <xsl:with-param name="filename" select="$OTHER_OUTPUT_FILENAME"/> 
    </xsl:call-template> 
</xsl:template> 

<xsl:template name="convertXMLHierarchyToFullPath"> 
    <xsl:param name="node"/> 
    <xsl:param name="filename"/> 

    <xsl:variable name="unorderedFullPath"> 
     <xsl:apply-templates select="$node"/> 
    </xsl:variable> 

    <xsl:result-document href="{$filename}" format="output"> 
     <xsl:for-each select="$unorderedFullPath/*"> 
      <xsl:sort select="@path" data-type="text"/> 
      <xsl:value-of select="@path"/> 
      <xsl:text>&#xA;</xsl:text> 
     </xsl:for-each> 
    </xsl:result-document> 
</xsl:template> 

<xsl:template match="*"> 
    <xsl:if test="not(*)"> 
     <leaf> 
      <xsl:attribute name="path"> 
       <xsl:for-each select="ancestor-or-self::*"> 
        <xsl:value-of select="name()"/> 
        <xsl:for-each select="@*"> 
         <xsl:sort select="name()" data-type="text"/> 
         <xsl:text>[</xsl:text> 
         <xsl:value-of select="name()"/> 
         <xsl:text>:</xsl:text> 
         <xsl:value-of select="."/> 
         <xsl:text>]</xsl:text> 
        </xsl:for-each> 
        <xsl:text>/</xsl:text> 
       </xsl:for-each> 
       <xsl:value-of select="."/> 
      </xsl:attribute> 
     </leaf> 
    </xsl:if> 
    <xsl:apply-templates select="*"/> 
</xsl:template> 

1

Tìm thấy bài này thời gian gần đây nhưng dù sao tôi sẽ chia sẻ giải pháp của tôi cho các loại vấn đề. Tôi đã có nhu cầu tương tự như @Vincent: so sánh 2 tập tin XML khác nhau và xem nhanh sự khác biệt giữa chúng. Một sự khác biệt nhanh có quá nhiều dòng khớp vì các tệp không được sắp xếp nên tôi quyết định sắp xếp các tệp bằng XSLT và sau đó so sánh hai tệp xml theo cách thủ công bằng cách sử dụng WinMerge ví dụ (một unix unix đơn giản cũng có thể thực hiện công việc).

Đây là XSLT rằng sắp xếp tập tin XML của tôi:.

<?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" encoding="UTF-8"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
      <xsl:apply-templates select="node()|@*"> 
        <xsl:sort select="name()" /> 
        <xsl:sort select="@*" /> 
        <xsl:sort select="*" /> 
        <xsl:sort select="text()" /> 
      </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

</xsl:stylesheet>