如何合并(覆盖)两个XML文档?

3
假设我有一个像这样的A文档:
<document>
    <element>
        <value>1</value>
        <wom>bat</wom>
    </element>
    <bar>
        <baz />
        <baz />
        <baz />
    </bar>
</document>

以及像这样的B文档:

<document>
    <element>
        <value>2</value>
    </element>
    <bar>

    </bar>
</document>

得到的结果如下:

<document>
    <element>
        <value>2</value>
        <wom>bat</wom>
    </element>
    <bar>

    </bar>
</document>

所需实现的目标是,使用文档 B 中提供的值来覆盖文档 A 中标记(如element)内的值,同时保留兄弟元素的值。但如果B中的标记为空(叶子节点),则我希望它在A中的对应项也被清空。我已经查看了此问题,但它是合并而不是覆盖。我该怎么解决这个问题? 澄清: 文档AB具有相同的结构,但B具有较少的元素。我必须清空B中为空的每个元素,在元素内部如果不为空就覆盖每个内部元素(参见我的示例)。

1
这是一个非常广泛的问题。您是否有要比较的特定元素(具有已知名称和路径)? - michael.hor257k
每个元素都需要合并。我已经指定了两个规则。 - Adam Arold
好的,这并不简单,除非元素具有唯一的名称。否则,您需要使用整个路径将每个元素与其对应项匹配。您正在使用XSLT 2.0吗? - michael.hor257k
你可以使用类似于这个样式表的东西,它可以合并任何结构的文档,并添加一个模板来简单地复制在文档B中为空的节点(*[not(normalize-space(child::node()))]),忽略文档A中的内容。 - helderdarocha
我可以使用任何解决我的问题的工具。 - Adam Arold
1个回答

5

一种方法是在导航 DocumentA 时,传递一个参数,该参数设置为 Document B 中等效节点。

首先匹配文档 A 的节点,并使用文档 B 的节点启动匹配过程。

   <xsl:template match="/">
      <xsl:apply-templates>
         <xsl:with-param name="parentB" select="document('DocB.xml')"/>
      </xsl:apply-templates>
   </xsl:template>

然后,您将拥有一个模板,该模板匹配A中的任何元素(以B中的当前(父)节点作为参数)。
   <xsl:template match="*">
      <xsl:param name="parentB"/>

要在B中找到相应的“子”节点,首先要找到A节点的当前位置(如果有多个同名子节点),然后检查该节点是否存在于父节点B下。
<xsl:variable name="posA">
   <xsl:number  />
</xsl:variable>
<xsl:variable name="nodeB" select="$parentB/*[local-name() = local-name(current())][number($posA)]"/>

接着,只需确定是复制 A 节点还是 B 节点。要复制 B 节点,B 节点必须存在且没有任何子元素(但可以有子文本节点,这些将被复制)。

<xsl:when test="$nodeB and not($nodeB/*)">
   <xsl:copy-of select="$nodeB/node()"/>
</xsl:when>

否则,继续处理A节点(将当前B节点作为参数传递)。
尝试使用此XSLT。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>

   <xsl:template match="/">
      <xsl:apply-templates>
         <xsl:with-param name="parentB" select="document('DocB.xml')"/>
      </xsl:apply-templates>
   </xsl:template>

   <xsl:template match="*">
      <xsl:param name="parentB"/>
      <xsl:variable name="posA">
          <xsl:number  />
       </xsl:variable>
      <xsl:variable name="nodeB" select="$parentB/*[local-name() = local-name(current())][number($posA)]"/>
      <xsl:copy>
         <xsl:choose>
            <xsl:when test="$nodeB and not($nodeB/*)">
               <xsl:copy-of select="$nodeB/node()"/>
            </xsl:when>
            <xsl:otherwise>
               <xsl:apply-templates select="@*|node()">
                  <xsl:with-param name="parentB" select="$nodeB"/>
               </xsl:apply-templates>
            </xsl:otherwise>
         </xsl:choose>
      </xsl:copy>
   </xsl:template>

   <xsl:template match="@*|node()[not(self::*)]">
      <xsl:copy>
         <xsl:apply-templates select="@*|node()"/>
      </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

请查看https://dev59.com/XU_Ta4cB1Zd3GeqPEdy7。该问题已被关闭,但答案会指引您正确的方向。 - Tim C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接