使用XSLT合并具有相同兄弟姐妹的2个XML文件

时间:2015-03-04 12:23:16

标签: xml xslt merge

我有两个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>

请提供以上疑问的帮助。

提前致谢

2 个答案:

答案 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)

根据您的说明进行编辑:

我相信您的要求可以简化为:

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

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

  3. 还有一个复杂因素,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 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>