如何在xslt中找到两个xml之间的区别

时间:2016-06-27 12:00:27

标签: xml xslt

我有xslt,其中包含一个像xml数据一样的变量

<reflection>
  <assemblies>... some data here..</assemblies>
<apis>
<api id="M:AdminService.AdminDetails.SetAdminRights(System.String)">
      <apidata name="SetAdminRights" group="member" subgroup="method" />
      <memberdata visibility="public" />
      <proceduredata virtual="false" />
      <parameters>
        <parameter name="rightsId">
          <type api="T:System.String" ref="true" />
        </parameter>
      </parameters>
      <returns>
        <type api="T:System.Boolean" ref="false" />
      </returns>
      <containers>
        <library assembly="AdminService" module="AdminService" kind="DynamicallyLinkedLibrary" />
        <namespace api="N:AdminService" />
        <type api="T:AdminService.AdminDetails" ref="true" />
      </containers>
    </api>

 ... many api tag like this.
</apis>
</reflection>

与另一个变量中的此数据相同。现在我需要找出有一些差异的api标签(这里api下的所有节点都应该是相同的,并且具有相同的属性和值。如果有任何变化,则需要对该api节点进行归档)。

我试过这个

<xsl:for-each  select="apis/api[not(api = $baseline/reflection/apis/api)]">

此foreach循环位于<xsl:template match="/reflection">下。没运气。它没有提供适当的数据。

如何实现这一目标?

2 个答案:

答案 0 :(得分:1)

使用像Saxon 9这样的XSLT 2.0处理器(可从http://saxon.sourceforge.net/和NuGet获得.NET)或者XmlPrime,你可以使用

<xsl:for-each select="apis/api[not($someApi in $baseline/reflection/apis/api satisfies deep-equal(., $someApi))]">

使用XSLT 1.0很难在单个XPath谓词中表达该条件,因为XSLT / XPath 1.0中不支持deep-equal

答案 1 :(得分:0)

我编写了这个XSLT差异表,它在两个xml文件之间进行了有序的区分。它比较输入xml中不存在的b.xml变量中的项。它是一个通用的xslt 1.0解决方案。但是,您必须修改工作表以查找b.xml中不在输入xml中的项目的反向。

https://github.com/sflynn1812/xslt-diff-turbo

因此,如果您将一个节点放在b.xml中并针对您正在尝试比较的另一个节点运行它,那么这将递归地比较两个xml文件。

或者你可以打电话给&#34; splitter&#34;模板直接。  用法:

<xsl:variable name="output">
              <xsl:call-template name="splitter">
              <xsl:with-param name="tree" select="*"/>
              <xsl:with-param name="comparer" select="$IDs2/*"></xsl:with-param>
              <xsl:with-param name="comparer-original" select="$IDs2/*"></xsl:with-param>
            </xsl:call-template>
            </xsl:variable>

库:

    <?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
  >
  <xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
  <xsl:variable name="file2" select="document('C:\TFS\Encore Utilities\ConfigurationComparisonLeafy\ConfigurationTransform.Comparison.Test\LiveComparison\lefthand.xml')" />
  <xsl:template match="comment()"/>
  <!-- Entry point into transform: file loading and processing occurs here -->
  <xsl:template  match="/">
    <xsl:variable name="IDs2" select="$file2/." />
    <xsl:variable name="output">
      <xsl:call-template name="splitter">
        <xsl:with-param name="tree" select="exsl:node-set(.)/*"/>
        <xsl:with-param name="comparer" select="exsl:node-set($IDs2/.)/*"></xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="output-remove">
      <compare-result>
        <xsl:copy-of select="exsl:node-set($output)//mismatch"/>
      </compare-result>
    </xsl:variable>
    <root>
      <xsl:copy-of select="exsl:node-set($output)"/>
    </root>
  </xsl:template>
  <!-- Main recursive looping algorithm through nodes or leaves in current branch -->
  <xsl:template name="splitter" >
    <xsl:param name="comparer"/>
    <xsl:param name="tree"/>
    <root>
      <xsl:if test="count(exsl:node-set($comparer)) = 1">
        <xsl:if test="count(exsl:node-set($tree)) = 1">
          <xsl:variable name="isMatch">
            <xsl:call-template name="match-node">
              <xsl:with-param name="node1" select="exsl:node-set($comparer)/."></xsl:with-param>
              <xsl:with-param name="node2" select="exsl:node-set($tree)/."></xsl:with-param>
            </xsl:call-template>
          </xsl:variable>
          <xsl:if test="exsl:node-set($isMatch)//mismatch/*">
            <compare>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($comparer)/."/>
              </mismatch>
            </compare>
            <tree>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($tree)/."/>
              </mismatch>
            </tree>
          </xsl:if>
          <xsl:if test="exsl:node-set($isMatch)//match/*">
            <xsl:if test="exsl:node-set($comparer)/* and exsl:node-set($tree)/*">
              <xsl:variable name="child-recurse">
                <xsl:call-template name="splitter">
                  <xsl:with-param name="comparer" select="exsl:node-set($comparer)/*"/>
                  <xsl:with-param name="tree" select="exsl:node-set($tree)/*"/>
                </xsl:call-template>
              </xsl:variable>
              <!-- post process child recurse on single node (post process function?) -->
              <tree>
                <mismatch>
                  <xsl:if test="exsl:node-set($child-recurse)//tree/mismatch/*">
                    <xsl:element name="{local-name($tree)}">
                      <xsl:copy-of select="exsl:node-set($tree)/@*"/>
                      <xsl:copy-of select="exsl:node-set($child-recurse)//tree/mismatch/*"/>                  
                    </xsl:element>
                  </xsl:if>
                </mismatch>
                <match>
                  <xsl:if test="exsl:node-set($child-recurse)//tree/match/*">
                    <xsl:element name="{local-name($tree)}">
                      <xsl:copy-of select="exsl:node-set($tree)/@*"/>
                      <xsl:copy-of select="exsl:node-set($child-recurse)//tree/match/*"/>
                    </xsl:element>
                  </xsl:if>
                </match>
              </tree>
              <compare>
                <mismatch>
                  <xsl:if test="exsl:node-set($child-recurse)//compare/mismatch/*">
                  <xsl:element name="{local-name($comparer)}">
                    <xsl:copy-of select="exsl:node-set($comparer)/@*"/>
                    <xsl:copy-of select="exsl:node-set($child-recurse)//compare/mismatch/*"/>
                  </xsl:element>
                  </xsl:if>
                </mismatch>
                <match>
                  <xsl:if test="exsl:node-set($child-recurse)//compare/match/*">
                    <xsl:element name="{local-name($comparer)}">
                      <xsl:copy-of select="exsl:node-set($comparer)/@*"/>
                      <xsl:copy-of select="exsl:node-set($child-recurse)//compare/match/*"/>
                    </xsl:element>
                  </xsl:if>
                </match>
              </compare>
            </xsl:if>
            <!-- one has children and one does not have children therefore mismatched -->
            <xsl:if test="(not(exsl:node-set($comparer)/*) and exsl:node-set($tree)/*) or (exsl:node-set($comparer)/* and not(exsl:node-set($tree)/*))">
              <tree>
                <mismatch>
                  <xsl:copy-of select="exsl:node-set($tree)"/>
                </mismatch>
              </tree>
              <compare>
                <mismatch>
                  <xsl:copy-of select="exsl:node-set($comparer)"/>
                </mismatch>
              </compare>
            </xsl:if>
            <!-- pair of match leaves -->
            <xsl:if test="not(exsl:node-set($comparer)/*) and not(exsl:node-set($tree)/*)">
              <tree>
                <match>
                      <xsl:copy-of select="exsl:node-set($tree)"/>
                </match>
              </tree>
              <compare>
                <match>
                    <xsl:copy-of select="exsl:node-set($comparer)"/>
                </match>
              </compare>
            </xsl:if>
          </xsl:if>
        </xsl:if>
        <xsl:if test="count(exsl:node-set($tree)) > 1">
          <xsl:variable name="range-left">
            <xsl:call-template name="Range">
              <xsl:with-param name="Array" select="exsl:node-set($tree)/."/>
              <xsl:with-param name="StartIndex" select="0" />
              <xsl:with-param name="EndIndex" select="(count(exsl:node-set($tree)/.) div 2)" />
            </xsl:call-template>
          </xsl:variable>
          <xsl:variable name="result-left">
            <xsl:call-template name="splitter">
              <xsl:with-param name="tree" select="exsl:node-set($range-left)/*"/>
              <xsl:with-param name="comparer" select="exsl:node-set($comparer)/."/>
            </xsl:call-template>
          </xsl:variable>
          <xsl:if test="not(exsl:node-set($result-left)//comparer/match/*)">
            <xsl:variable name="range-right">
              <xsl:call-template name="Range">
                <xsl:with-param name="Array" select="exsl:node-set($tree)/."/>
                <xsl:with-param name="StartIndex" select="count(exsl:node-set($tree)/.) div 2" />
                <xsl:with-param name="EndIndex" select="count(exsl:node-set($tree)/.)" />
              </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="result-right">
              <xsl:call-template name="splitter">
                <xsl:with-param name="tree" select="exsl:node-set($range-right)/*"/>
                <xsl:with-param name="comparer" select="exsl:node-set($comparer)/."/>
              </xsl:call-template>
            </xsl:variable>
            <tree>
              <match>
                <xsl:copy-of select="exsl:node-set($result-right)//tree/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-left)//tree/mismatch/*"/>
                <xsl:copy-of select="exsl:node-set($result-right)//tree/mismatch/*"/>
              </mismatch>
            </tree>
            <compare>
              <match>
                <xsl:copy-of select="exsl:node-set($result-right)//compare/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-right)//compare/mismatch/*"/>
              </mismatch>
            </compare>
          </xsl:if>
          <xsl:if test="exsl:node-set($result-left)//comparer/match/*">
            <tree>
              <match>
                <xsl:copy-of select="exsl:node-set($result-left)//tree/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-left)//tree/mismatch/*"/>
              </mismatch>
            </tree>
            <compare>
              <match>
                <xsl:copy-of select="exsl:node-set($result-left)//compare/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-left)//compare/mismatch/*"/>
              </mismatch>
            </compare>
          </xsl:if>
        </xsl:if>
      </xsl:if>
      <xsl:if test="count(exsl:node-set($comparer)) > 1">
        <xsl:if test="count(exsl:node-set($tree)) = 1">
          <xsl:variable name="range-left">
            <xsl:call-template name="Range">
              <xsl:with-param name="Array" select="exsl:node-set($comparer)/."/>
              <xsl:with-param name="StartIndex" select="0" />
              <xsl:with-param name="EndIndex" select="(count(exsl:node-set($comparer)/.) div 2)" />
            </xsl:call-template>
          </xsl:variable>
          <xsl:variable name="result-left">
            <xsl:call-template name="splitter">
              <xsl:with-param name="tree" select="exsl:node-set($tree)/."/>
              <xsl:with-param name="comparer" select="exsl:node-set($range-left)/*"/>
            </xsl:call-template>
          </xsl:variable>
          <xsl:variable name="range-right">
            <xsl:call-template name="Range">
              <xsl:with-param name="Array" select="exsl:node-set($comparer)/."/>
              <xsl:with-param name="StartIndex" select="count(exsl:node-set($comparer)/.) div 2" />
              <xsl:with-param name="EndIndex" select="count(exsl:node-set($comparer)/.)" />
            </xsl:call-template>
          </xsl:variable>
          <xsl:if test="not(exsl:node-set($result-left)//tree/match/*)">
            <xsl:variable name="result-right">
              <xsl:call-template name="splitter">
                <xsl:with-param name="tree" select="exsl:node-set($tree)/."/>
                <xsl:with-param name="comparer" select="exsl:node-set($range-right)/*"/>
              </xsl:call-template>
            </xsl:variable>
            <tree>
              <match>
                <xsl:copy-of select="exsl:node-set($result-right)//tree/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-right)//tree/mismatch/*"/>
              </mismatch>
            </tree>
            <compare>
              <match>
                <xsl:copy-of select="exsl:node-set($result-right)//compare/match/*"/>
              </match>
              <mismatch>
                  <xsl:copy-of select="exsl:node-set($result-left)//compare/mismatch/*"/>
                  <xsl:copy-of select="exsl:node-set($result-right)//compare/mismatch/*"/>
              </mismatch>
            </compare>
          </xsl:if>
          <xsl:if test="exsl:node-set($result-left)//tree/match/*">
            <tree>
              <match>
                <xsl:copy-of select="exsl:node-set($result-left)//tree/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-left)//tree/mismatch/*"/>
              </mismatch>
            </tree>
            <compare>
              <match>
                <xsl:copy-of select="exsl:node-set($result-left)//compare/match/*"/>
              </match>
              <mismatch>
                <xsl:copy-of select="exsl:node-set($result-left)//compare/mismatch/*"/>
                <xsl:copy-of select="exsl:node-set($range-right)/*"/>
              </mismatch>
            </compare>
          </xsl:if>
        </xsl:if>
      </xsl:if>
      <xsl:if test="count(exsl:node-set($tree)) > 1 and count(exsl:node-set($comparer)) > 1">
        <xsl:variable name="range-left">
          <xsl:call-template name="Range">
            <xsl:with-param name="Array" select="exsl:node-set($tree)/."/>
            <xsl:with-param name="StartIndex" select="0" />
            <xsl:with-param name="EndIndex" select="(count(exsl:node-set($tree)/.) div 2)" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="range-right">
          <xsl:call-template name="Range">
            <xsl:with-param name="Array" select="exsl:node-set($tree)/."/>
            <xsl:with-param name="StartIndex" select="count(exsl:node-set($tree)/.) div 2" />
            <xsl:with-param name="EndIndex" select="count(exsl:node-set($tree)/.)" />
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="result-left">
          <xsl:call-template name="splitter">
            <xsl:with-param name="tree" select="exsl:node-set($range-left)/*"/>
            <xsl:with-param name="comparer" select="exsl:node-set($comparer)/."/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:if test="exsl:node-set($result-left)//compare/mismatch/*">
        <xsl:variable name="result-right">
          <xsl:call-template name="splitter">
            <xsl:with-param name="tree" select="exsl:node-set($range-right)/*"/>
            <xsl:with-param name="comparer" select="exsl:node-set($result-left)//compare/mismatch/*"/>
          </xsl:call-template>
        </xsl:variable>
          <tree>
            <mismatch>
              <xsl:copy-of select="exsl:node-set($result-left)//tree/mismatch/*"/>
              <xsl:copy-of select="exsl:node-set($result-right)//tree/mismatch/*"/>
            </mismatch>
            <match>
              <xsl:copy-of select="exsl:node-set($result-left)//tree/match/*"/>
              <xsl:copy-of select="exsl:node-set($result-right)//tree/match/*"/>
            </match>
          </tree>
          <compare>
            <mismatch>
                <xsl:copy-of select="exsl:node-set($result-right)//compare/mismatch/*"/>
            </mismatch>
            <match>
                <xsl:copy-of select="exsl:node-set($result-left)//compare/match/*"/>
                <xsl:copy-of select="exsl:node-set($result-right)//compare/match/*"/>
            </match>
          </compare>
        </xsl:if>
        <xsl:if test="not(exsl:node-set($result-left)//compare/mismatch/*)">
          <tree>
            <mismatch>
              <xsl:copy-of select="exsl:node-set($range-right)/*"/>
            </mismatch>
            <match>
              <xsl:copy-of select="exsl:node-set($result-left)//tree/match/*"/>
            </match>
          </tree>
          <compare>
            <mismatch>
            </mismatch>
            <match>
              <xsl:copy-of select="exsl:node-set($result-left)//compare/match/*"/>
            </match>
          </compare>
        </xsl:if>
        </xsl:if>
    </root>
  </xsl:template>
  <!-- sub function for matching single nodes -->
  <xsl:template name="match-node">
    <xsl:param name="node1"></xsl:param>
    <xsl:param name="node2"></xsl:param>
    <xsl:if test="name($node1) = name($node2)">
      <xsl:variable name="attribute-mismatch">
        <xsl:call-template name="attribute-value-mismatch">
          <xsl:with-param name="attributes1" select="$node1/@*"></xsl:with-param>
          <xsl:with-param name="attributes2" select="$node2/@*"></xsl:with-param>
        </xsl:call-template>
      </xsl:variable>
      <xsl:if test="count(exsl:node-set($attribute-mismatch)//attribute) = 0">
        <xsl:if test="(not(exsl:node-set(translate(normalize-space($node1/text()),'&#xa;', ''))) and not(exsl:node-set(translate(normalize-space($node2/text()),'&#xa;', '')))) or (translate(normalize-space($node1/text()),'&#xa;', '') = translate(normalize-space($node2/text()),'&#xa;', ''))">
          <match>
            <xsl:copy-of select="$node1 | @*"></xsl:copy-of>
          </match>
        </xsl:if>
        <xsl:if test="not((not(exsl:node-set(translate(normalize-space($node1/text()),'&#xa;', ''))) and not(exsl:node-set(translate(normalize-space($node2/text()),'&#xa;', '')))) or (translate(normalize-space($node1/text()),'&#xa;', '') = translate(normalize-space($node2/text()),'&#xa;', '')))">
          <mismatch>
            <xsl:copy-of select="$node1 | @*"></xsl:copy-of>
          </mismatch>
        </xsl:if>
      </xsl:if>
      <xsl:if test="count(exsl:node-set($attribute-mismatch)//attribute) > 0">
        <mismatch>
          <xsl:apply-templates mode="copy-no-namespaces" select="$node1 | @*" />
        </mismatch>
      </xsl:if>
    </xsl:if>
    <xsl:if test="name($node1) != name($node2)">
      <mismatch>
        <xsl:apply-templates mode="copy-no-namespaces" select="$node1 | @*" />
      </mismatch>
    </xsl:if>
  </xsl:template>
  <!-- sub function for matching attributes in two nodes - outputs an attribute node only when an attibute mismatches -->
  <xsl:template name="attribute-value-mismatch">
    <xsl:param name="attributes1" />
    <xsl:param name="attributes2" />
    <attribute-match>
      <xsl:if test="(count($attributes1) != count($attributes2))">
        <attribute />
      </xsl:if>
      <xsl:if test="(count($attributes1) = count($attributes2))">
        <xsl:for-each select="$attributes1">
          <xsl:variable name="attribute1" select="."/>
          <xsl:variable name="result">
            <attroot>
              <xsl:for-each select="$attributes2">
                <xsl:if test="name(.) = name($attribute1/.)">
                  <xsl:if test="not(. = $attribute1/.)">
                    <not-matched-name/>
                  </xsl:if>
                </xsl:if>
                <xsl:if test="name(.) != name($attribute1/.)">
                  <not-matched-name/>
                </xsl:if>
              </xsl:for-each>
            </attroot>
          </xsl:variable>
          <xsl:if test="count(exsl:node-set($result)//not-matched-name) = count(exsl:node-set($attributes2))">
            <attribute />
          </xsl:if>
        </xsl:for-each>
      </xsl:if>
    </attribute-match>
  </xsl:template>
  <!-- sub function: exclude node from branch -->
  <xsl:template name="excludeNodeFromTree">
    <xsl:param name="branch-layer"></xsl:param>
    <xsl:param name="node"></xsl:param>
    <xsl:if test="name(exsl:node-set($branch-layer)[1]) = name($node)">
      <xsl:variable name="attribute-value-mismatch-count-exclude">
        <xsl:call-template name="attribute-value-mismatch">
          <xsl:with-param name="attributes1" select="$branch-layer[1]/@*"/>
          <xsl:with-param name="attributes2" select="$node/@*"/>
        </xsl:call-template>
      </xsl:variable>
      <xsl:if test="count(exsl:node-set($attribute-value-mismatch-count-exclude)/attribute-match/attribute) = 0">
        <sub-tree>
          <xsl:for-each select="exsl:node-set($branch-layer[1])/preceding-sibling::*">
            <xsl:copy-of select="."/>
          </xsl:for-each>
          <xsl:for-each select="exsl:node-set($branch-layer[1])/following-sibling::*">
            <xsl:copy-of select="."/>
          </xsl:for-each>
        </sub-tree>
      </xsl:if>
      <xsl:if test="count(exsl:node-set($attribute-value-mismatch-count-exclude)/attribute-match/attribute) > 0">
        <xsl:call-template name="excludeNodeFromTree">
          <xsl:with-param name="branch-layer" select="$branch-layer[1]/following-sibling::*"></xsl:with-param>
          <xsl:with-param name="node" select="$node"></xsl:with-param>
        </xsl:call-template>
      </xsl:if>
    </xsl:if>
    <xsl:if test="name($branch-layer[1]) != name($node) and exsl:node-set($branch-layer)[1]/following-sibling::*">
      <xsl:call-template name="excludeNodeFromTree">
        <xsl:with-param name="branch-layer" select="$branch-layer[1]/following-sibling::*"/>
        <xsl:with-param name="node" select="$node"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="not(exsl:node-set($branch-layer)[1]/following-sibling::*)">
      <sub-tree>
      </sub-tree>
    </xsl:if>
  </xsl:template>
  <xsl:template match="*" mode="copy-no-namespaces">
    <xsl:element name="{local-name()}">
      <xsl:copy-of select="@*"/>
      <xsl:apply-templates select="node()" mode="copy-no-namespaces"/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="comment()| processing-instruction()" mode="copy-no-namespaces">
    <xsl:copy/>
  </xsl:template>
  <xsl:template name="Range">
    <xsl:param name="Array" />
    <xsl:param name="StartIndex"/>
    <xsl:param name="EndIndex"/>
    <xsl:copy-of select="$Array[position() > $StartIndex and not(position() > $EndIndex)]" />
  </xsl:template>
</xsl:stylesheet>