我有两个XML文件(XML1和XML2:粘贴在下面),需要将XML2到XML1的更改合并到第三个文件(比如XML3),以便:
1)如果在两个文件中找到相同的属性 1.a)检查属性值是否不同,然后用XML2中的值覆盖XML1的值。 例如MasterControl在XML1中启用,而在XML2中禁用。 合并后,预期输出在XML3中为MasterControl为Disabled。
1.b)如果两个文件中的属性值相同或为空,则在XML3中合并后XML1中没有更改。
2)如果XML 1具有XML2中不存在的额外属性,则在合并文件中添加此类属性。
3)如果XML 2具有XML1中不存在的额外属性,则在合并文件中添加此类属性。
我已经将“XSLT合并到XML文件的合并”XSLT to Merge 2 XML Files提供了链接,它解决了以下大部分要求:
合并后,我在规则ID树中面临问题,其中所有规则ID的层次路径相同。 在上面提到的链接中,为每个元素计算路径,并根据它完成合并。
我正在搜索通用解决方案,因为这只是一小段XML文件。文件中有大约300多个参数。 XML:1
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="D:\test.xsl"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>Enabled</MasterControl>
<ReservedPool></ReservedPool>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Enabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="3" Label="Label">
<RuleCriteria>
<CellType>Pico</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
XML2:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="D:\test.xsl"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>Disabled</MasterControl>
<ReservedPool></ReservedPool>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
XSLT:
<?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" />
<xsl:param name="aXmlPath" select="''" />
<xsl:param name="aDoc" select="document('q_xml2.xml')" />
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()" />
</xsl:copy>
</xsl:template>
<!-- text nodes will be checked against doc A -->
<xsl:template match="*[not(*)]/text()">
<xsl:variable name="path">
<xsl:call-template name="calculatePath" />
</xsl:variable>
<xsl:variable name="valueFromA">
<xsl:call-template name="nodeValueByPath">
<xsl:with-param name="path" select="$path" />
<xsl:with-param name="context" select="$aDoc" />
</xsl:call-template>
</xsl:variable>
<xsl:choose>
<!-- either there is something at that path in doc A -->
<xsl:when test="starts-with($valueFromA, 'found:')">
<!-- remove prefix added in nodeValueByPath, see there -->
<xsl:value-of select="substring-after($valueFromA, 'found:')" />
</xsl:when>
<!-- or we take the value from doc B -->
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- this calcluates a simpe path for a node -->
<xsl:template name="calculatePath">
<xsl:for-each select="..">
<xsl:call-template name="calculatePath" />
</xsl:for-each>
<xsl:if test="self::*">
<xsl:value-of select="concat(name(), '/')" />
</xsl:if>
</xsl:template>
<!-- this retrieves a node value by its simple path -->
<xsl:template name="nodeValueByPath">
<xsl:param name="path" select="''" />
<xsl:param name="context" select="''" />
<xsl:if test="contains($path, '/') and count($context)">
<xsl:variable name="elemName" select="substring-before($path, '/')" />
<xsl:variable name="nextPath" select="substring-after($path, '/')" />
<xsl:variable name="currContext" select="$context/*[name() = $elemName][1]" />
<xsl:if test="$currContext">
<xsl:choose>
<xsl:when test="contains($nextPath, '/')">
<xsl:call-template name="nodeValueByPath">
<xsl:with-param name="path" select="$nextPath" />
<xsl:with-param name="context" select="$currContext" />
</xsl:call-template>
</xsl:when>
<xsl:when test="not($currContext/*)">
<!-- always add a prefix so we can detect
the case "exists in A, but is empty" -->
<xsl:value-of select="concat('found:', $currContext/text())" />
</xsl:when>
</xsl:choose>
</xsl:if>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
实际输出:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="D:\test.xsl"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>Disabled</MasterControl>
<ReservedPool/>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="3" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
见上面的ReservedPool属性终止而没有结束标记。 此外,所有规则ID都与相同的CellType树合并,而两个xml对于每个规则ID都具有不同的CellType。
预期产出:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="D:\test.xsl"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>Disabled</MasterControl>
<ReservedPool></ReservedPool>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="3" Label="Label">
<RuleCriteria>
<CellType>Pico</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
请提供以上疑问的帮助。
提前致谢
答案 0 :(得分:0)
您可以使用命名空间。
编辑第一个文件:
<NCP xmlns="file/a">
编辑第二个文件:
<NCP xmlns="file/b">
在xslt中定义名称空间:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:f1="file/a" xmlns:f2="file/b">
现在您可以选择节点并指定它们所属的命名空间(和文件):
<xsl:variable name="rootFromFirstFile" select="f1:NCP" />
答案 1 :(得分:0)
根据您的说明进行编辑:
我相信您的要求可以简化为:
从XML1获取不的所有参数都具有相应的参数 XML2中的(覆盖)参数;
从XML2中获取所有参数(这些参数要么覆盖了 XML1中存在的参数,或者不存在的额外参数 存在于XML1)。
还有一个复杂因素,Rules
需要与CellType
匹配,而其他参数(MasterControl
和ReservedPool
)则需要与其名称匹配。
考虑到这一点,请尝试:
XSLT 1.0
<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:strip-space elements="*"/>
<xsl:param name="doc2" select="document('q_xml2.xml')" />
<xsl:variable name="doc2-param-names">
<xsl:for-each select="$doc2/NCP/NCPList/NCP/ParametersList/*">
<name><xsl:value-of select="name()"/></name>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="doc2-cell-types" select="$doc2/NCP/NCPList/NCP/ParametersList/Rule/RuleCriteria/CellType" />
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ParametersList">
<xsl:copy>
<!-- local parameters (other than Rules), with no external override -->
<xsl:apply-templates select="*[not(self::Rule)] [not(name()=exsl:node-set($doc2-param-names)/name)]"/>
<!-- local Rules, with no external override -->
<xsl:apply-templates select="Rule[not(RuleCriteria/CellType=$doc2-cell-types)]"/>
<!-- ALL external items -->
<xsl:apply-templates select="$doc2/NCP/NCPList/NCP/ParametersList/*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
测试:
XML INPUT
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>Enabled</MasterControl>
<ReservedPool/>
<OLD>OLD</OLD>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Enabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="3" Label="Label">
<RuleCriteria>
<CellType>Pico</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
文件q_xml2.xml
<?xml version="1.0" encoding="UTF-8"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<MasterControl>DISABLED</MasterControl>
<ReservedPool/>
<NEW>NEW</NEW>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>
<强>结果强>
<?xml version="1.0" encoding="UTF-8"?>
<NCP>
<NCPList>
<NCP ID="1" Label="LabelName">
<ParametersList>
<OLD>OLD</OLD>
<Rule ID="3" Label="Label">
<RuleCriteria>
<CellType>Pico</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<MasterControl>DISABLED</MasterControl>
<ReservedPool/>
<NEW>NEW</NEW>
<Rule ID="1" Label="Label">
<RuleCriteria>
<CellType>Macro</CellType>
</RuleCriteria>
<Assignment>Disabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
<Rule ID="2" Label="Label">
<RuleCriteria>
<CellType>Micro</CellType>
</RuleCriteria>
<Assignment>Enabled</Assignment>
<ReAssignment>Disabled</ReAssignment>
</Rule>
</ParametersList>
</NCP>
</NCPList>
</NCP>