Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
XSLT合并两个XML文件_Xml_Apache_Xslt_Merge_Xalan - Fatal编程技术网

XSLT合并两个XML文件

XSLT合并两个XML文件,xml,apache,xslt,merge,xalan,Xml,Apache,Xslt,Merge,Xalan,我需要使用XSLT合并两个XML文件。转换发生在包含要合并的XML文件列表的XML文件上 list.xml <?xml version="1.0" encoding="UTF-8" ?> <files> <file>..\src\main\resources\testOne.xml</file> <file>..\src\main\resources\testTwo.xml</file> </files

我需要使用XSLT合并两个XML文件。转换发生在包含要合并的XML文件列表的XML文件上

list.xml

<?xml version="1.0" encoding="UTF-8" ?>
<files>
    <file>..\src\main\resources\testOne.xml</file>
    <file>..\src\main\resources\testTwo.xml</file>
</files>
以下是我要合并的两个模板:

<xsl:template name="merge_nodes">
    <xsl:param name="fnNewDeept"/>
    <xsl:param name="snNewDeept"/>

    <xsl:for-each select="$fnNewDeept">
        <xsl:call-template name="merge_node">
            <xsl:with-param name="first-node" select="$fnNewDeept"/>
            <xsl:with-param name="second-node" select="$snNewDeept"/>
        </xsl:call-template>
    </xsl:for-each>
</xsl:template>

<xsl:template name="merge_node">
    <xsl:param name="first-node" />
    <xsl:param name="second-node" />

    <xsl:element name="{name(current())}">
        <xsl:for-each select="$second-node/@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:if test="$first-node = '' and not(boolean($first-node/*) and boolean($second-node/*))">
            <xsl:value-of select="$second-node"/>
        </xsl:if>

        <xsl:for-each select="$first-node/@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:if test="not(boolean($first-node/*) and boolean($second-node/*))">
            <xsl:value-of select="$first-node"/>
        </xsl:if>

        <xsl:choose>
            <xsl:when test="boolean($first-node/*) or boolean($second-node/*)">     
                <xsl:choose>                                                        
                    <xsl:when test="boolean($first-node/*/*)">                      
                        <xsl:call-template name="merge_nodes">                      
                            <xsl:with-param name="fnNewDeept" select="$first-node/*"/>
                            <xsl:with-param name="snNewDeept" select="$second-node/*"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        2. Value: <xsl:value-of select="current()/*"/>
                        2. Current: <xsl:value-of select="name(current()/*)"/>
                        2. First: <xsl:value-of select="name($first-node/*)"/>
                        2. Second: <xsl:value-of select="name($second-node/*)"/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                1. Value: <xsl:value-of select="current()"/>
                1. Current: <xsl:value-of select="name(current())"/>
                1. First: <xsl:value-of select="name($first-node)"/>
                1. Second: <xsl:value-of select="name($second-node)"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:element>
</xsl:template>
值、当前值、第一个值和第二个值仅用于调试。 还有我的两个XML:

<?xml version="1.0" encoding="UTF-8" ?>
<first x="1">
    <second param="wt" second="true">
        <third>abc</third>
        <third>def</third>
    </second>
    <fourth>
        <fifth x="1">hij</fifth>
        <fifth>klm</fifth>
    </fourth>
    <sixth>qrs</sixth>
</first>
二,

我希望首选第一个文件,以便将第二个文件合并到第一个文件中。不应出现重复的元素

预期产出:

<?xml version="1.0" encoding="UTF-8" ?>
<first x="1" y="2">
    <second param="wt" second="true">
        <third>abc</third>
        <third>def</third>
        <third>asd</third>
    </second>
    <fourth>
        <fifth x="1">hij</fifth>
        <fifth>klm</fifth>
        <fifth y="2">tuv</fifth>
        <fifth>wxy</fifth>
    </fourth>
    <sixth>qrs</sixth>
    <sixth>678</sixth>
    <sixth>910</sixth>
</first>
我得到的输出:

<?xml version="1.0" encoding="windows-1252"?><first y="2" x="1">
<second param="wt" second="true">
                            2. Value: abc
                            2. Current: third
                            2. First: third
                            2. Second: third</second>
<fourth param="wt" second="true">
                            2. Value: hij
                            2. Current: fifth
                            2. First: third
                            2. Second: third</fourth>
<sixth param="wt" second="true">
                            2. Value: 
                            2. Current: 
                            2. First: third
                            2. Second: third</sixth>
</first>
我不知道如何同时沿着两棵树运行,以便复制元素。有人有什么想法吗? 我只能用阿帕奇·萨兰。我使用最新版本2.7.2


编辑:因为已经有误解了。转换必须适用于类似的XML文件,这是一个大问题。

我试图通过调整元素的比较来理解您的要求以及中解释和显示的Oliver Becker实现的部分,如果名称、命名空间和内容匹配,则没有子元素的元素被认为是等效的,如果具有复杂内容的元素(即元素子元素)具有相同的名称和命名空间,则认为它们是等效的,并且通过调整元素合并以复制第二个文档中的属性,$node2/@*首先被复制,因为我认为您希望第一个文档中的属性具有优先权

<xsl:output method="xml" indent="yes"/>
    <xsl:variable name="doc" select="doc('merge2.xml')"/>
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="first">
        <xsl:copy>
        <xsl:apply-templates select="@*"/>
            <xsl:apply-templates select="$doc/first/@*"/>
            <xsl:apply-templates/>

        </xsl:copy>
    </xsl:template>
    <xsl:template match="second">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
            <xsl:copy-of select="$doc/first/second/third[ not(.= current()/third)]"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="fourth">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:apply-templates/>
            <xsl:copy-of select="$doc/first/fourth/fifth[ not(.= current()/fifth)]"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="sixth">
        <xsl:copy>

            <xsl:apply-templates/>

        </xsl:copy>
        <xsl:copy-of select="$doc/first/sixth[ not(.= current()/sixth)]"/>
    </xsl:template>
CHeck it
我已经在Saxon 9.8 HE上在线测试了这一点,但只是为了能够以可执行的方式共享代码和结果,因为XSLT 2或3处理器允许我轻松地内联第二个文档。所以我有

<!--
   Merging two XML files
   Version 1.6
   LGPL (c) Oliver Becker, 2002-07-05
   obecker@informatik.hu-berlin.de
-->
<xslt:transform xmlns:xslt="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://informatik.hu-berlin.de/merge" version="1.0" exclude-result-prefixes="m">


<!-- Normalize the contents of text, comment, and processing-instruction
     nodes before comparing?
     Default: yes -->
<xslt:param name="normalize" select="'yes'" />

<!-- Don't merge elements with this (qualified) name -->
<xslt:param name="dontmerge" />

<!-- If set to true, text nodes in file1 will be replaced -->
<xslt:param name="replace" select="false()" />

<!-- Variant 1: Source document looks like
     <?xml version="1.0"?>
     <merge xmlns="http://informatik.hu-berlin.de/merge">
        <file1>file1.xml</file1>
        <file2>file2.xml</file2>
     </merge>         
     The transformation sheet merges file1.xml and file2.xml.
-->
<xslt:template match="m:merge">
   <xslt:variable name="file1" select="string(m:file1)" />
   <xslt:variable name="file2" select="string(m:file2)" />
   <xslt:message>
      <xslt:text />Merging '<xslt:value-of select="$file1" />
      <xslt:text />' and '<xslt:value-of select="$file2" />'<xslt:text />
   </xslt:message>
   <xslt:if test="$file1='' or $file2=''">
      <xslt:message terminate="yes">
         <xslt:text>No files to merge specified</xslt:text>
      </xslt:message>
   </xslt:if>
   <xslt:call-template name="m:merge">
      <xslt:with-param name="nodes1" select="document($file1,/*)/node()" />
      <xslt:with-param name="nodes2" select="document($file2,/*)/node()" />
   </xslt:call-template>
</xslt:template>


<!-- Variant 2:
     The transformation sheet merges the source document with the
     document provided by the parameter "with".
-->
<xslt:param name="with" />

<!-- for testing I inline the second document -->
<xslt:param name="with-doc">
<first y="2">
    <second param="123" second="false">
        <third>asd</third>
        <third>def</third>
    </second>
    <fourth>
        <fifth y="2">tuv</fifth>
        <fifth>wxy</fifth>
    </fourth>
    <sixth>678</sixth>
    <sixth>910</sixth>
</first>    
</xslt:param>

<xslt:template match="*">
   <xslt:message>
      <xslt:text />Merging input with '<xslt:value-of select="$with" />
      <xslt:text>'</xslt:text>
   </xslt:message>
   <!--<xslt:if test="string($with)=''">
      <xslt:message terminate="yes">
         <xslt:text>No input file specified (parameter 'with')</xslt:text>
      </xslt:message>
   </xslt:if>-->

   <xslt:call-template name="m:merge">
      <xslt:with-param name="nodes1" select="/node()" />
      <!-- <xslt:with-param name="nodes2" select="document($with,/*)/node()" /> -->
      <xslt:with-param name="nodes2" select="$with-doc/node()" />
   </xslt:call-template>
</xslt:template>


<!-- ============================================================== -->

<!-- The "merge" template -->
<xslt:template name="m:merge">
   <xslt:param name="nodes1" />
   <xslt:param name="nodes2" />

   <xslt:choose>
      <!-- Is $nodes1 resp. $nodes2 empty? -->
      <xslt:when test="count($nodes1)=0">
         <xslt:copy-of select="$nodes2" />
      </xslt:when>
      <xslt:when test="count($nodes2)=0">
         <xslt:copy-of select="$nodes1" />
      </xslt:when>

      <xslt:otherwise>
         <!-- Split $nodes1 and $nodes2 -->
         <xslt:variable name="first1" select="$nodes1[1]" />
         <xslt:variable name="rest1" select="$nodes1[position()!=1]" />
         <xslt:variable name="first2" select="$nodes2[1]" />
         <xslt:variable name="rest2" select="$nodes2[position()!=1]" />
         <!-- Determine type of node $first1 -->
         <xslt:variable name="type1">
            <xslt:apply-templates mode="m:detect-type" select="$first1" />
         </xslt:variable>

         <!-- Compare $first1 and $first2 -->
         <xslt:variable name="diff-first">
            <xslt:call-template name="m:compare-nodes">
               <xslt:with-param name="node1" select="$first1" />
               <xslt:with-param name="node2" select="$first2" />
            </xslt:call-template>
         </xslt:variable>

         <xslt:choose>
            <!-- $first1 != $first2 -->
            <xslt:when test="$diff-first='!'">
               <!-- Compare $first1 and $rest2 -->
               <xslt:variable name="diff-rest">
                  <xslt:for-each select="$rest2">
                     <xslt:call-template name="m:compare-nodes">
                        <xslt:with-param name="node1" select="$first1" />
                        <xslt:with-param name="node2" select="." />
                     </xslt:call-template>
                  </xslt:for-each>
               </xslt:variable>

               <xslt:choose>
                  <!-- $first1 is in $rest2 and 
                       $first1 is *not* an empty text node  -->
                  <xslt:when test="contains($diff-rest,'=') and not($type1='text' and normalize-space($first1)='')">
                     <!-- determine position of $first1 in $nodes2
                          and copy all preceding nodes of $nodes2 -->
                     <xslt:variable name="pos" select="string-length(substring-before( $diff-rest,'=')) + 2" />
                     <xslt:copy-of select="$nodes2[position() &lt; $pos]" />
                     <!-- merge $first1 with its equivalent node -->
                     <xslt:choose>
                        <!-- Elements: merge -->
                        <xslt:when test="$type1='element'">
                           <xslt:element name="{name($first1)}" namespace="{namespace-uri($first1)}">
                              <xslt:copy-of select="$first1/namespace::*" />
                              <xslt:copy-of select="$first2/namespace::*" />
                              <xslt:copy-of select="$first2/@*"/>
                              <xslt:copy-of select="$first1/@*" />
                              <xslt:call-template name="m:merge">
                                 <xslt:with-param name="nodes1" select="$first1/node()" />
                                 <xslt:with-param name="nodes2" select="$nodes2[position()=$pos]/node()" />
                              </xslt:call-template>
                           </xslt:element>
                        </xslt:when>
                        <!-- Other: copy -->
                        <xslt:otherwise>
                           <xslt:copy-of select="$first1" />
                        </xslt:otherwise>
                     </xslt:choose>

                     <!-- Merge $rest1 and rest of $nodes2 -->
                     <xslt:call-template name="m:merge">
                        <xslt:with-param name="nodes1" select="$rest1" />
                        <xslt:with-param name="nodes2" select="$nodes2[position() &gt; $pos]" />
                     </xslt:call-template>
                  </xslt:when>

                  <!-- $first1 is a text node and replace mode was
                       activated -->
                  <xslt:when test="$type1='text' and $replace">
                     <xslt:call-template name="m:merge">
                        <xslt:with-param name="nodes1" select="$rest1" />
                        <xslt:with-param name="nodes2" select="$nodes2" />
                     </xslt:call-template>
                  </xslt:when>

                  <!-- else: $first1 is not in $rest2 or
                       $first1 is an empty text node -->
                  <xslt:otherwise>
                     <xslt:copy-of select="$first1" />
                     <xslt:call-template name="m:merge">
                        <xslt:with-param name="nodes1" select="$rest1" />
                        <xslt:with-param name="nodes2" select="$nodes2" />
                     </xslt:call-template>
                  </xslt:otherwise>
               </xslt:choose>
            </xslt:when>

            <!-- else: $first1 = $first2 -->
            <xslt:otherwise>
               <xslt:choose>
                  <!-- Elements: merge -->
                  <xslt:when test="$type1='element'">
                     <xslt:element name="{name($first1)}" namespace="{namespace-uri($first1)}">
                        <xslt:copy-of select="$first2/namespace::*" />
                        <xslt:copy-of select="$first1/namespace::*" />
                        <xslt:copy-of select="$first2/@*" />
                        <xslt:copy-of select="$first1/@*" />
                        <xslt:call-template name="m:merge">
                           <xslt:with-param name="nodes1" select="$first1/node()" />
                           <xslt:with-param name="nodes2" select="$first2/node()" />
                        </xslt:call-template>
                     </xslt:element>
                  </xslt:when>
                  <!-- Other: copy -->
                  <xslt:otherwise>
                     <xslt:copy-of select="$first1" />
                  </xslt:otherwise>
               </xslt:choose>

               <!-- Merge $rest1 and $rest2 -->
               <xslt:call-template name="m:merge">
                  <xslt:with-param name="nodes1" select="$rest1" />
                  <xslt:with-param name="nodes2" select="$rest2" />
               </xslt:call-template>
            </xslt:otherwise>
         </xslt:choose>
      </xslt:otherwise>
   </xslt:choose>
</xslt:template>


<!-- Comparing single nodes: 
     if $node1 and $node2 are equivalent then the template creates a 
     text node "=" otherwise a text node "!" -->
<xslt:template name="m:compare-nodes">
   <xslt:param name="node1" />
   <xslt:param name="node2" />
   <xslt:variable name="type1">
      <xslt:apply-templates mode="m:detect-type" select="$node1" />
   </xslt:variable>
   <xslt:variable name="type2">
      <xslt:apply-templates mode="m:detect-type" select="$node2" />
   </xslt:variable>

   <xslt:choose>
      <!-- Are $node1 and $node2 complex element nodes with the same name? -->
      <xslt:when test="$type1='element' and $type2='element' and $node1/* and $node2/* and local-name($node1)=local-name($node2) and namespace-uri($node1)=namespace-uri($node2) and name($node1)!=$dontmerge and name($node2)!=$dontmerge">
          <xslt:text>=</xslt:text>
      </xslt:when>

      <!-- Are $node1 and $node2 simple elements with the same name and same content -->
      <xslt:when test="$type1='element' and $type2='element' and not($node1/*) and not($node2/*) and local-name($node1)=local-name($node2) and namespace-uri($node1)=namespace-uri($node2) and $node1 = $node2 and name($node1)!=$dontmerge and name($node2)!=$dontmerge">
          <xslt:text>=</xslt:text>
      </xslt:when>

      <!-- Other nodes: test for the same type and content -->
      <xslt:when test="$type1!='element' and $type1=$type2 and name($node1)=name($node2) and ($node1=$node2 or ($normalize='yes' and normalize-space($node1)= normalize-space($node2)))">=</xslt:when>

      <!-- Otherwise: different node types or different name/content -->
      <xslt:otherwise>!</xslt:otherwise>
   </xslt:choose>
</xslt:template>


<!-- Type detection, thanks to M. H. Kay -->
<xslt:template match="*" mode="m:detect-type">element</xslt:template>
<xslt:template match="text()" mode="m:detect-type">text</xslt:template>
<xslt:template match="comment()" mode="m:detect-type">comment</xslt:template>
<xslt:template match="processing-instruction()" mode="m:detect-type">pi</xslt:template>

</xslt:transform>
另一方面,由于我使用了第二个内联示例,其中删除了空白,因此可以假设在使用document函数的正常情况下不会发生这种情况,然后在内联示例上模拟xml:space=preserve,结果是

<first y="2" x="1">
    <second param="wt" second="true">
        <third>abc</third>
        <third>asd</third><third>def</third>
    </second>
    <fourth>
        <fifth x="1">hij</fifth>
        <fifth>klm</fifth>
    <fifth y="2">tuv</fifth><fifth>wxy</fifth></fourth>
    <sixth>qrs</sixth>
<sixth>678</sixth><sixth>910</sixth></first>
<first y="2" xml:space="preserve" x="1">
    <second param="wt" second="true">
        <third>abc</third>
        <third>asd</third>
        <third>def</third>
    </second>
    <fourth>
        <fifth x="1">hij</fifth>
        <fifth>klm</fifth>
    <fifth y="2">tuv</fifth>
        <fifth>wxy</fifth>
    </fourth>
    <sixth>qrs</sixth>
<sixth>678</sixth>
    <sixth>910</sixth>
</first>

看起来也很有希望。因此,尝试分别进行调整,将其更改为使用with参数和document函数,就像在最初的示例中一样,这样您可能会得到想要的结果,至少对于您显示的两个示例是这样的。很难说它是否是一个通用的解决方案,因为我认为合并的整个想法在很大程度上取决于如何准确比较节点的明确规范。

在XSLT 1的辉煌和古老时代,我认为一个叫Oliver Becker的人实现了合并算法的一个相当复杂的XSLT实现,我还没有找到原件,但似乎是一份副本,你可能想研究一下,你是否可以将它应用到你的问题上,或者使它适应你的需要,或者至少使用它的想法来改进你自己的样式表。@Martin谢谢你。我需要仔细看看。到目前为止,只有第二个XML的内容是在第一个XML下编写的。您需要更详细地解释或指定哪些重复元素不应该出现,这对您的情况来说意味着什么,即如何确定不同节点的重复元素。例如,Oliver Becker的原始页面,我现在在,对于元素的合并,节点说明两个元素节点被视为等价的,它们的本地名称相等,它们的命名空间URI相等,并且它们的所有属性都相等。我不确定何时要合并,它似乎是基于元素名称和位置,但在比较元素时似乎忽略了属性。如果元素具有相同的名称并且属于相同的命名空间,则它们是相同的。属性当前未被考虑,因为如果属性已经存在,Xalan处理器似乎不会复制属性。值无关紧要。您好,感谢您的回答/作业!对不起,我一定是问错问题了。我需要能够合并所有类似的XML。因此,模板必须是通用的。非常感谢!我认为这就是解决办法。我正在测试代码。如果一切顺利,我会把你的答案记为解决办法。
<first y="2" x="1">
   <second param="wt" second="true">
      <third>abc</third>
      <third>asd</third>
      <third>def</third>
   </second>
   <fourth>
      <fifth x="1">hij</fifth>
      <fifth>klm</fifth>
      <fifth y="2">tuv</fifth>
      <fifth>wxy</fifth>
   </fourth>
   <sixth>qrs</sixth>
   <sixth>678</sixth>
   <sixth>910</sixth>
</first>
<first y="2" xml:space="preserve" x="1">
    <second param="wt" second="true">
        <third>abc</third>
        <third>asd</third>
        <third>def</third>
    </second>
    <fourth>
        <fifth x="1">hij</fifth>
        <fifth>klm</fifth>
    <fifth y="2">tuv</fifth>
        <fifth>wxy</fifth>
    </fourth>
    <sixth>qrs</sixth>
<sixth>678</sixth>
    <sixth>910</sixth>
</first>