【发布时间】:2015-03-04 12:23:15
【问题描述】:
我有两个 XML 文件(XML1 和 XML2:粘贴在下面),需要将 XML2 到 XML1 的更改合并到第三个文件(比如 XML3),这样:
1) 如果在两个文件中发现相同的属性,则 1.a) 检查属性值是否不同,然后用 XML2 的值覆盖 XML1 的值。 例如MasterControl 在 XML1 中启用,而在 XML2 中禁用。 合并后,预期的输出是 MasterControl 在 XML3 中禁用。
1.b) 如果两个文件中的属性值相同或为空,则在 XML3 中合并后 XML1 中没有变化。
2) 如果 XML 1 具有 XML2 中不存在的额外属性,则在合并文件中添加此类属性。
3) 如果 XML 2 具有 XML1 中不存在的额外属性,则在合并文件中添加此类属性。
我已经引用了链接“XSLT to Merge 2 XML Files”XSLT to Merge 2 XML Files,它解决了大部分要求,但以下要求除外:
合并后,我在规则 ID 树中遇到了问题,其中所有规则 ID 的层次路径都相同。 在上面提到的链接中,为每个元素计算路径并基于它,合并完成。
我正在寻找通用解决方案,因为这只是 XML 文件的小 sn-p。文件中有大约 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>
请在上述疑问中提供帮助。
提前致谢
【问题讨论】:
-
ReservedPool在您的示例中不是属性,<ReservedPool/>与<ReservedPool></ReservedPool>完全相同。 -
你应该再澄清一件事:你能通过它们的 ID 值匹配规则吗? --附言在 XML 中,attribute 一词具有非常精确的含义;您要合并的东西是不是属性。
-
规则可以与 CellType "Macro" / "Micro"/ "Pico" 等匹配。两个 XML 中相同 CellType 的 ID 可以不同。谢谢你告诉我这件事。实际上是我第一次使用 XML 和 XSLT。