【问题标题】:How to merge xml files using xslt and remove empty elements如何使用 xslt 合并 xml 文件并删除空元素
【发布时间】:2015-03-26 08:47:20
【问题描述】:

我有几个要合并的文件,忽略空元素。

例如: 文件 1:

    <ds>
    <sk>
    <f1>1</f1>
    <f2>2</f2>
    </sk>
    <sk>
    <f1>3</f1>
    <f2>4</f2>
    </sk>
    </ds>    

文件 2:

    <ds>
    <ac>
    <g1>9</f1>
    <g2>10</f2>
    </ac>
    <ac>
    <g1>11</f1>
    <g2>12</f2>
    </ac>
    </ds>    

和文件 3:

    <ds>
    <rs>
    <k1>A</f1>
    <k2>B</f2>
    <k3></k3>
    </rs>
    <rs>
    <k1>C</f1>
    <k2>B</f2>
    <k3/>
    </rs>
    </ds>    

我想作为输出:

    <ds>
    <sk>
    <f1>1</f1>
    <f2>2</f2>
    </sk>
    <sk>
    <f1>3</f1>
    <f2>4</f2>
    </sk>
    <ac>
    <g1>9</f1>
    <g2>10</f2>
    </ac>
    <ac>
    <g1>11</f1>
    <g2>12</f2>
    </ac>
    <rs>
    <k1>A</f1>
    <k2>B</f2>
    </rs>
    <rs>
    <k1>C</f1>
    <k2>B</f2>
    </rs>
    </ds>    

我已经用 xslt 试过了:

    <?xml version="1.0" ?>
    <xsl:transform
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xml:space="preserve"
        version="1.0">
        <xsl:output indent="yes"/>

     <xsl:template match="/ds">
         <xsl:copy-of select="document('File1')/ds/sk"/>
         <xsl:copy-of select="document('File2')/ds/ac"/>
         <xsl:copy-of select="document('File3')/ds/rs"/>
     </xsl:template>

    </xsl:transform>

输入是 File1。文件已合并,但我不知道如何删除 File3 之类的空元素。 有什么想法吗?

【问题讨论】:

  • 欢迎来到 SO。不幸的是,我无法真正看到您在做什么:您的 xslt 不完整,xsl:for-each 没有正文,也没有关闭。我假设你的代码正在做你所说的“合并”?
  • 有一个错误,不是for-each而是copy-of。
  • 谢谢,但您仍应关闭它们以获取合法的 XML。 (复制和粘贴经过测试的代码通常是个好主意。)

标签: xml xslt xpath merge


【解决方案1】:

假设您的所有 XML 文件实际上格式正确,您应该从使用 XSLT 身份转换开始

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

然后,不要使用xsl:for-each,而是使用xsl:apply-templates。

<xsl:apply-templates select="document('File1')/ds/sk" />
<xsl:apply-templates select="document('File2')/ds/ac" />
<xsl:apply-templates select="document('File3')/ds/rs" />

注意,如果 File1 恰好是 XSLT 本身的输入文件,您可能不一定需要使用 document 函数。

然后要删除空元素,只需添加这样的模板:

<xsl:template match="*[not(*)][not(normalize-space())]" />

试试这个 XSLT

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes" />

     <xsl:template match="/">
         <xsl:apply-templates select="document('File1')/ds/sk" />
         <xsl:apply-templates select="document('File2')/ds/ac" />
         <xsl:apply-templates select="document('File3')/ds/rs" />
     </xsl:template>

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

    <xsl:template match="*[not(*)][not(normalize-space())]" />
</xsl:transform>

【讨论】:

  • Thks,我已经尝试了给定的 xslt,但根本没有输出任何 xml。注意:我最初的帖子中有一个错误,我使用的是 copy-of 而不是 for-each ...并且我正在使用 msxml 进行转换。
  • 随时修改您的初始帖子以纠正您的错误。另外,我对我的答案做了一个小的编辑。您确实需要确保 document 函数引用每个文件的正确文件路径。如果您只输入File1.xml,它将(可能)在与 XSLT 相同的文件夹中查找 XML。谢谢!
  • File1.xml 位于同一目录中,但无论如何我使用绝对路径。我用的是c:\test\File1.xml,用file:///c:/test/File1.xml试了一下,结果一样,没有输出文件...
  • 如果替换xsl:apply-templates而不是xsl:copy-of,我的答案会产生输出吗?
【解决方案2】:

我不知道为什么,但使用这个 xslt 解决了问题

 <xsl:template match="/">
     <xsl:copy>
     <xsl:apply-templates select="document('File1')/ds/sk" />
     <xsl:apply-templates select="document('File2')/ds/ac" />
     <xsl:apply-templates select="document('File3')/ds/rs" />
     </xls:copy>
 </xsl:template>

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

<xsl:template match="*[not(*)][not(normalize-space())]" />

注意:使用 Altova(使用 msxml 作为解析器)时,您的表现良好。在我的代码(powerbuilder)中,我需要使用这个...

【讨论】:

  • 与蒂姆的回答有什么不同?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-02
相关资源
最近更新 更多