【问题标题】:compare two xml files with xslt?用 xslt 比较两个 xml 文件?
【发布时间】:2020-03-04 17:43:07
【问题描述】:

我有 2 个 xml 文件。如何使用 xslt 比较两个文件是否相等?如果不相等意味着在第二个 xml 中发生了哪些更改?

【问题讨论】:

    标签: xml xslt compare


    【解决方案1】:

    在 XPath 2.0 中,您可以简单地使用 fn:deep-equal

    遵循 XSLT 1.0 中的相同模式,此样式表:

    <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>
    

    输出:

    Documents are different
    

    【讨论】:

    • @Alejandro:很好的尝试,但您没有考虑属于命名空间的节点、纯空白节点、命名空间节点、PI 节点和注释节点。我们知道,两个节点可能有不同的名称,但仍然是等价的——比如:h:htmlhtml。要求比较两个 XML 文档是否“相等”的人通常不知道他们究竟要求的是什么。
    • @Dimitre:你说得对。我错误地翻译了 XPath 2.0 node-name()。我会纠正的。关于其他节点类型:因为应该更精确地定义“平等”,所以我采用了deep-equal 定义,它不考虑 cmets 或 PI 子节点,在范围命名空间或空格中,仅用于比较 elements 的文本节点.
    【解决方案2】:

    XSLT 最适合一种 XML 方言转换为另一种。

    为了比较 XML 文件,我会在您的平台上使用 XML 解析器并使用它比较文档。

    比较两者是可能的,但如果您有其他选择,我建议您不要这样做。

    【讨论】:

      【解决方案3】:

      这是我编写的样式表,用于比较具有不同节点和属性顺序的两个 XML 文件。它将生成两个文本文件,其中包含所有叶节点路径的有序列表。使用任何文本比较工具来找出差异或增强 XSLT 以执行您想要的操作。

      <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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多