2010-12-28 33 views
5

Tôi có 2 tệp xml. Làm thế nào tôi có thể so sánh cả hai tập tin bằng nhau hay không bằng cách sử dụng xslt? Nếu không bằng nhau có nghĩa là những thay đổi đã xảy ra trong xml thứ hai?so sánh hai tệp xml với xslt?

Trả lời

0

XSLT được sử dụng tốt nhất để chuyển đổi một phương ngữ XML sang một phương ngữ khác.

Để so sánh các tệp XML, tôi sẽ sử dụng trình phân tích cú pháp XML trên nền tảng của bạn và so sánh các tài liệu sử dụng nó.

Đó là có thể để so sánh hai, nhưng tôi khuyên bạn nên chống lại nó, nếu bạn có các tùy chọn khác.

4

Trong XPath 2.0, bạn có thể sử dụng đơn giản fn:deep-equal.

Tiếp theo cùng một khuôn mẫu trong XSLT 1.0, kiểu này:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:param name="pSource2" select="'emp2.xml'"/> 
    <xsl:template match="/*"> 
     <xsl:variable name="vDeep-equal"> 
      <xsl:apply-templates select="." mode="deep-equal"> 
       <xsl:with-param name="pTarget" select="document($pSource2)/*"/> 
      </xsl:apply-templates> 
     </xsl:variable> 
     <xsl:choose> 
      <xsl:when test="normalize-space($vDeep-equal)"> 
       <xsl:text>Documents are different</xsl:text> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:text>Documents are deep equal</xsl:text> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template match="*" mode="deep-equal"> 
     <xsl:param name="pTarget"/> 
     <xsl:choose> 
      <xsl:when test="$pTarget/self::* and 
          local-name()=local-name($pTarget) and 
          namespace-uri()=namespace-uri($pTarget) and 
          count(@*)=count($pTarget/@*) and 
          count(*|text()[normalize-space()]) = 
           count($pTarget/*| 
            $pTarget/text()[normalize-space()])"> 
       <xsl:for-each select="@*"> 
        <xsl:if test="$pTarget/@*[name()=name(current())] != ."> 
         <xsl:text>false</xsl:text> 
        </xsl:if> 
       </xsl:for-each> 
       <xsl:for-each select="*|text()[normalize-space()]"> 
        <xsl:variable name="vPosition" select="position()"/> 
        <xsl:apply-templates select="." mode="deep-equal"> 
         <xsl:with-param name="pTarget" 
             select="($pTarget/*| 
               $pTarget/text() 
                [normalize-space()]) 
                  [$vPosition]"/> 
        </xsl:apply-templates> 
       </xsl:for-each> 
      </xsl:when> 
      <xsl:otherwise>false</xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 
    <xsl:template match="text()" mode="deep-equal"> 
     <xsl:param name="pTarget"/> 
     <xsl:if test="not($pTarget/self::text() and 
         string() = string($pTarget))"> 
      <xsl:text>false</xsl:text> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Output:

Documents are different 
+0

@Alejandro: nỗ lực tốt, nhưng bạn đã không được coi là các nút thuộc trong một không gian tên, nút khoảng trắng chỉ, nút namespace, nút PI và bình luận các nút. Như chúng ta đã biết, hai nút có thể có các tên khác nhau và vẫn tương đương - như: 'h: html' và' html'. Những người yêu cầu so sánh hai tài liệu XML cho "bình đẳng" thường không biết chính xác họ đang yêu cầu điều gì. –

+0

@Dimitre: Bạn nói đúng về tên. Tôi đã dịch sai XPath 2.0 'node-name()'. Tôi sẽ sửa nó. Về loại nút khác: vì "bình đẳng" nên được định nghĩa chính xác hơn, tôi lấy định nghĩa 'sâu bằng' mà không xem xét các chú thích hoặc PI, trong phạm vi không gian tên cũng như khoảng trắng chỉ các nút văn bản cho các phần tử ** so sánh **. –

0

Đây là kiểu tôi đã viết để so sánh hai tập tin XML với thứ tự khác nhau trong các nút và các thuộc tính. Nó sẽ tạo ra hai tập tin văn bản, có chứa danh sách theo thứ tự của tất cả các đường dẫn 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> 

</xsl:stylesheet>