如何合并两个文档而不使用XSLT 1.0覆盖原始元素?

时间:2017-05-19 11:49:50

标签: xslt xslt-1.0

我是XSLT的新手,我需要将xslt脚本插入第三方软件,该软件使用XSLT 1.0转换xml文档。

我的任务是获取文档A.xml并插入文档B.xml中的每个元素,但前提是A中的文本尚不存在。输出应该生成为文档C.xml。

示例A.xml:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Name>SCHAME.table_name</Name>
        <Location>oracle:TNS_1</Location>
        <Citation>
            <Title>Title 1</Title>
            <Description/>
        </Citation>
        <metadataDate>20170418</metadataDate>
    </Table>
</metadata>

示例B.xml:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Citation>
            <Title>Template Title</Title>
            <Abstract>Template Abstract</Abstract>
            <Description>Template Description</Description>
        </Citation>
        <MetadataDate>20160131</MetadataDate>
    </Table>
</metadata>

C.xml的预期输出是:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Name>SCHAME.table_name</Name>
        <Location>oracle:TNS_1</Location>
        <Citation>
            <Title>Title 1</Title>
            <Abstract>Template Abstract</Abstract>
            <Description>Template Description</Description>
        </Citation>
        <metadataDate>20170418</metadataDate>
    </Table>
</metadata>

有三件事是重要的:

  1. B可以包含元素,这些元素在A中不存在但必须复制到C(例如元数据/表格/引文/摘要)
  2. 带有文本的A元素不得在C中用B中的文本覆盖(例如元数据/表格/引文/标题)。然后,A中的空元素必须用B中的文本填充(例如元数据/表格/引用/描述)
  3. xml只是一个示例,我的真实xml文件中有超过一百个不同的标签,这些只是描述我的问题的示例。所以我的问题的任何解决方案都必须适用于比我的样本xml中的标签更多的标签。
  4. 我不需要正在运行的解决方案,任何提示如何为XSLT初学者解决这个问题都会很好。

2 个答案:

答案 0 :(得分:0)

此处的问题是如何识别两个文件中的对应的元素。我假设每个元素最多出现一次,因此我们可以简单地通过元素名称来识别相应的元素。 我的解决方案遵循John Bollinger的想法:

  1. 创建一个template.xml文件,其中包含所需输出结构中所有个可能元素。
  2. XSL脚本遍历模板的所有元素。
  3. 它在文件A和B中查找此元素的值。
  4. 如果值A不为空,请将其取出,否则从B中取值。
  5. 这是我使用的模板:

    <?xml version="1.0"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    
        <xsl:output method="xml" indent="yes" />        
        <xsl:strip-space elements="*"/>
    
        <!-- Parameters -->
        <xsl:param name="aFile"     select="'a.xml'" />
        <xsl:param name="bFile"     select="'b.xml'" />
    
        <!-- Variables -->
        <xsl:variable name="aDoc" select="document( $aFile, . )"/>
        <xsl:variable name="bDoc" select="document( $bFile, . )"/>
    
        <!-- Locate elements by name in both files -->
        <xsl:key name="elementsByName" match="*" use="name()" />
    
        <!-- Root-Template -->
        <xsl:template match="/">
            <xsl:comment>
                <xsl:value-of select="concat( 'Merge of ', $aFile, ' with ', $bFile )" />
            </xsl:comment>
    
            <xsl:apply-templates />
        </xsl:template>
    
        <!-- Merge all elements -->
        <xsl:template match="*">
            <xsl:variable name="elemName"   select="name()" />
            <xsl:variable name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" />
            <xsl:variable name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" />
    
            <xsl:copy>
                <xsl:choose>
                    <xsl:when test="$aValue != ''">
                        <xsl:value-of select="$aValue" />
                    </xsl:when>
    
                    <xsl:when test="$bValue != ''">
                        <xsl:value-of select="$bValue" />
                    </xsl:when>
                </xsl:choose>
    
                <xsl:apply-templates select="*" />
            </xsl:copy>
        </xsl:template>
    
    </xsl:stylesheet>
    

    以下XSL将上述模板作为输入:

    <?xml version="1.0" encoding="UTF-8"?>
    <!--Merge of a.xml with b.xml-->
    <metadata>
       <Table>
          <Name>SCHAME.table_name</Name>
          <Location>oracle:TNS_1</Location>
          <Citation>
             <Title>Title 1</Title>
             <Abstract>Template Abstract</Abstract>
             <Description>Template Description</Description>
          </Citation>
          <metadataDate>20170418</metadataDate>
       </Table>
    </metadata>
    

    转型的结果是:

    xsl:key

    唯一的&#34;技巧&#34;在此代码中使用key()和相应的xsl:copy函数。这允许我们在文件A和B中找到相应的元素(相同的名称)。

    此脚本将模板中的所有元素复制到输出文件C. 要更改此行为,只需移动xsl:when内的{{1}}指令

答案 1 :(得分:0)

我尝试单独给出一个改进的答案,以避免与那里的有用评论混淆。

除了那里的“通用”方法(匹配元素名称)之外,我们现在也有特定的模板。

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*"/>

    <!-- Parameters -->
    <xsl:param name="aFile"     select="'a.xml'" />
    <xsl:param name="bFile"     select="'b.xml'" />

    <!-- Variables -->
    <xsl:variable name="aDoc"   select="document( $aFile, . )"/>
    <xsl:variable name="bDoc"   select="document( $bFile, . )"/>

    <!-- Locate elements by name in both files -->
    <xsl:key name="elementsByName" match="*" use="name()" />

    <!-- Root-Template -->
    <xsl:template match="/">
        <xsl:comment>
            <xsl:value-of select="concat( 'Merge of ', $aFile, ' with ', $bFile )" />
        </xsl:comment>

        <xsl:apply-templates />
    </xsl:template>

    <!-- Merge specific elements -->
    <xsl:template match="metadata/Table/Description">
        <xsl:call-template name="mergeElement">
            <xsl:with-param name="aValue" select="$aDoc/metadata/Table/Description/text()" />
            <xsl:with-param name="bValue" select="$bDoc/metadata/Table/Description/text()" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="metadata/Table/Citation/Description">
        <xsl:call-template name="mergeElement">
            <xsl:with-param name="aValue" select="$aDoc/metadata/Table/Citation/Description/text()" />
            <xsl:with-param name="bValue" select="$bDoc/metadata/Table/Citation/Description/text()" />
        </xsl:call-template>
    </xsl:template>

    <!-- Merge unique elements -->
    <xsl:template match="*" priority="-10">
        <xsl:variable name="elemName"   select="name()" />

        <xsl:call-template name="mergeElement">
            <xsl:with-param name="aValue" select="$aDoc/key('elementsByName', $elemName)/text()" />
            <xsl:with-param name="bValue" select="$bDoc/key('elementsByName', $elemName)/text()" />
        </xsl:call-template>
    </xsl:template>

    <!-- Use A or B -->
    <xsl:template name="mergeElement">
        <xsl:param name="aValue" />
        <xsl:param name="bValue" />

        <xsl:copy>
            <xsl:choose>
                <xsl:when test="$aValue != ''">
                    <xsl:value-of select="$aValue" />
                </xsl:when>

                <xsl:when test="$bValue != ''">
                    <xsl:value-of select="$bValue" />
                </xsl:when>
            </xsl:choose>

            <xsl:apply-templates select="*" />
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

对于测试,我更改了模板和A和B,并在Table中直接添加了另一个Description元素:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Name />
        <Description/>  <!-- NEW: not unique element name -->
        <Location/>
        <Citation>
            <Title />
            <Abstract />
            <Description/>
        </Citation>
        <metadataDate/>
    </Table>
</metadata>

档案A:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Name>SCHAME.table_name</Name>
        <Location>oracle:TNS_1</Location>
        <Description>Table description A</Description> <!-- NEW -->
        <Citation>
            <Title>Title 1</Title>
            <Description/>
        </Citation>
        <metadataDate>20170418</metadataDate>
    </Table>
</metadata>

档案B:

<?xml version="1.0" encoding="UTF-8"?>
<metadata>
    <Table>
        <Description>Table description B</Description> <!-- NEW -->
        <Citation>
            <Title>Template Title</Title>
            <Abstract>Template Abstract</Abstract>
            <Description>Template Description</Description>
        </Citation>
        <MetadataDate>20160131</MetadataDate>
    </Table>
</metadata>

生成的文件C:

<?xml version="1.0" encoding="UTF-8"?>
<!--Merge of a.xml with b.xml-->
<metadata>
   <Table>
      <Name>SCHAME.table_name</Name>
      <Description>Table description A</Description> <!-- NEW -->
      <Location>oracle:TNS_1</Location>
      <Citation>
         <Title>Title 1</Title>
         <Abstract>Template Abstract</Abstract>
         <Description>Template Description</Description>
      </Citation>
      <metadataDate>20170418</metadataDate>
   </Table>
</metadata>

显然,这不是一般的解决方案。 但是,如果非唯一元素的数量与总数相比较低,您将受益于通用模板。只有非独特元素必须单独“设置”。