【问题标题】:Merge 2 XML files with identical siblings using XSLT使用 XSLT 合并 2 个具有相同兄弟的 XML 文件
【发布时间】: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 在您的示例中不是属性,&lt;ReservedPool/&gt;&lt;ReservedPool&gt;&lt;/ReservedPool&gt;完全相同
  • 你应该再澄清一件事:你能通过它们的 ID 值匹配规则吗? --附言在 XML 中,attribute 一词具有非常精确的含义;您要合并的东西是不是属性。
  • 规则可以与 CellType "Macro" / "Micro"/ "Pico" 等匹配。两个 XML 中相同 CellType 的 ID 可以不同。谢谢你告诉我这件事。实际上是我第一次使用 XML 和 XSLT。

标签: xml xslt merge


【解决方案1】:

您可以使用命名空间。

编辑第一个文件:

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

【讨论】:

  • 感谢@Maxim Lazarev 的回复,但我不明白它将如何解决我上面提到的问题。预期的输出也会更新。
【解决方案2】:

根据您的说明进行了编辑:

我相信您的要求可以浓缩为:

  1. 从 XML1 中获取所有有对应的参数 XML2 中的(覆盖)参数;

  2. 从 XML2 中获取 all 参数(这些参数要么覆盖 XML1 中存在的参数,或不存在的额外参数 存在于 XML1) 中。

还有一个更复杂的地方,Rules 需要通过它们的 CellType 匹配,而其他参数(MasterControlReservedPool)需要通过它们的名称匹配。

考虑到这一点,尝试:

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

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

【讨论】:

  • michael - 我已经编辑了所有要求的问题。请再次检查。我需要将所有属性从 XML2 合并到 XML1。
  • 我已经使用建议的 XSLT 进行了测试,但这里的问题是只有规则树被合并。 不是从 XML2 合并的。在输出文件中,MasterControl 已启用,而预期的输出是它应该被禁用,因为 XML2 中提到了相同的内容。
  • @Rash10 我的结果不同。您使用的是哪个处理器?
猜你喜欢
  • 2015-03-20
  • 1970-01-01
  • 1970-01-01
  • 2021-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多