我有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">
下。没运气。它没有提供适当的数据。
如何实现这一目标?
答案 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()),'
', ''))) and not(exsl:node-set(translate(normalize-space($node2/text()),'
', '')))) or (translate(normalize-space($node1/text()),'
', '') = translate(normalize-space($node2/text()),'
', ''))">
<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()),'
', ''))) and not(exsl:node-set(translate(normalize-space($node2/text()),'
', '')))) or (translate(normalize-space($node1/text()),'
', '') = translate(normalize-space($node2/text()),'
', '')))">
<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>