【问题标题】:XSLT Remove blank namespaces without assigning elements to different or default namespaceXSLT 删除空白名称空间而不将元素分配给不同的名称空间或默认名称空间
【发布时间】:2015-10-05 02:53:15
【问题描述】:

我有以下 XML:

<ns1:A xmlns:ns1="http://www.namespace1.com" xmlns:ns2="http://www.namespace2.com">
<B>
    <C>123</C>
</B>
<ns2:D>
    <E>456</E>
</ns2:D>
</ns1:A>

我想使用以下 XSLT 对其进行转换:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:xs="http://www.w3.org/2001/XMLSchema" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            exclude-result-prefixes="xsl xs xsi">

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:strip-space elements="*" />

<xsl:template match="/">
    <xsl:apply-templates />
</xsl:template>

<xsl:template match="*">
    <xsl:element name="{local-name()}" namespace="{namespace-uri()}">
        <xsl:copy-of select="@*|namespace::*[name()]"/>
        <xsl:if test="text() != ''">
            <xsl:value-of select="concat(text(), 'xyz')"/>
        </xsl:if>
        <xsl:apply-templates select="*"/>
    </xsl:element>
</xsl:template>
</xsl:stylesheet>

输出包含元素 B 和 E 的不需要的空白命名空间:

<A xmlns="http://www.namespace1.com" xmlns:ns1="http://www.namespace1.com" xmlns:ns2="http://www.namespace2.com">
<B xmlns="">
  <C>123xyz</C>
</B>
<D xmlns="http://www.namespace2.com">
  <E xmlns="">456xyz</E>
</D>
</A>

我希望删除输出中的空白名称空间。我看过很多关于分配默认命名空间或将元素分配给某个父命名空间的帖子。然而,这不是我想要的。在输出中,我确实希望元素 B、C 和 E 保留在“空白”命名空间中,因为这是相应的 XSD 所需要的(由于遗留原因,我无法更新 XSD)。所以想要的输出是:

 <A xmlns="http://www.namespace1.com" xmlns:ns1="http://www.namespace1.com" xmlns:ns2="http://www.namespace2.com">
 <B>
   <C>123xyz</C>
 </B>
 <D xmlns="http://www.namespace2.com">
   <E>456xyz</E>
 </D>
</A>

如何摆脱输出中的空白命名空间?我还想指出,XSLT 将应用于具有不同模式结构的不同输入 XML,因此 XSLT 必须尽可能通用,而不需要对元素或命名空间名称进行硬编码。

编辑:

查看 M. Kay 和 M. Hor 的答案后,我意识到我之前的“期望”输出是不正确的。正如 M. Hor 所解释的,默认命名空间是继承的,如我之前的“所需”输出中列出的那样,这也不是我想要的。 (对不起,不正确的“期望”输出)。

我(更正后)想要的输出是不使用任何默认命名空间,而是使用命名空间前缀:

<ns1:A xmlns:ns1="http://www.namespace1.com" xmlns:ns2="http://www.namespace2.com">
   <B>
      <C>123xyz</C>
   </B>
   <ns2:D>
      <E>456xyz</E>
   </ns2:D>
</ns1:A>

我已经更新了我的 XSLT 作为下面这个问题的答案,它输出了我想要的。

谢谢! IK

【问题讨论】:

    标签: xml xslt namespaces


    【解决方案1】:

    (输入和输出相同,只是因为我使用的是简化的 XSLT 暂时)。

    当您的 XSLT 实际上没有改变任何东西时,很难理解这个问题。出于声明的目的,identity transform 模板应该可以正常工作:

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="2.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <!-- identity transform -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    编辑

    响应您的编辑,显示不同的所需输出:

    你这么说:

    我确实希望元素 B、C 和 E 保留在“空白”命名空间中

    但这不是在您想要的输出中发生的情况:

    <A xmlns="http://www.namespace1.com" xmlns:ns1="http://www.namespace1.com"
       xmlns:ns2="http://www.namespace2.com">
       <B>
          <C>123xyz</C>
       </B>
       <D xmlns="http://www.namespace2.com">
          <E>456xyz</E>
       </D>
    </A>
    

    这里,元素BC 从它们的A 祖先继承它们的命名空间。同样,E 元素与其 D 父元素位于同一命名空间中。

    这就是声明一个默认命名空间时的工作方式:声明范围内的所有元素都放置在该命名空间中。为了将B 放在无命名空间中,您必须进行显式异常。这就是您在实际结果中看到 &lt;B xmlns=""&gt; 的原因。

    【讨论】:

    • 你说得对,我最初的问题有点不清楚 - 我已经更新了我的问题以使其更清楚。身份转换不是我想要的,因为输出与输入 XML 不同。对我来说主要问题是去掉输出 xmlns="" 中的空白命名空间,同时保持元素 B、C 和 E 没有分配命名空间。
    • @ILK 您可以简单地将xsl:if 及其内容添加到身份转换模板中。或者(更可能在真实场景中)添加更具体的模板以在必要时覆盖身份转换。但是您提到的“主要问题”无法按照您建议的方式解决 - 请参阅我的答案的编辑以了解原因。
    • 谢谢 - 您的解释让我意识到我想要的输出(使用默认命名空间)也不是我想要的。我已经用更正后的所需输出编辑了我的问题。
    【解决方案2】:

    当你说xmlns="" 声明是不可取的,大概你的意思是你希望B 元素在命名空间http://www.namespace1.com 中。所以你需要做的就是把它放在那个命名空间中。您使用指令&lt;xsl:element name="{local-name()}" namespace="{namespace-uri()}"&gt; 创建了元素,因此您将新的 B 元素显式放置在与旧元素相同的命名空间中(即没有命名空间)。如果你想把它放在不同的命名空间中,只需更改此指令即可。

    【讨论】:

    • 感谢您的回复 - 我意识到我之前想要的输出不正确。我已经更新了我的问题以列出更正后的所需输出。
    • 最好打开一个新问题。修改答案以响应修改后的问题太令人困惑了。
    【解决方案3】:

    下面的 XSLT 将输出命名空间前缀而不输出默认命名空间,从而避免了输出中的 xmlns=""。

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exclude-result-prefixes="xsl xs xsi">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>
    
    <xsl:template match="*">
        <xsl:element name="{name()}" namespace="{namespace-uri()}">
            <xsl:copy-of select="@*|namespace::*[name()]"/>
            <xsl:if test="text() != ''">
                <xsl:value-of select="concat(text(), 'xyz')"/>
            </xsl:if>
            <xsl:apply-templates select="*"/>
        </xsl:element>
    </xsl:template>
    
    </xsl:stylesheet>
    

    基本上,我没有使用 local-name(),而是使用 name() 来包含前缀。

    现在的输出是:

    <ns1:A xmlns:ns1="http://www.namespace1.com" xmlns:ns2="http://www.namespace2.com">
    <B>
      <C>123xyz</C>
    </B>
    <ns2:D>
      <E>456xyz</E>
    </ns2:D>
    </ns1:A>
    

    注意:这是使用 Saxon Home Edition 9.4 测试的。我注意到其他 XSLT 处理器可能仍会输出 xmlns=""。

    【讨论】:

    • 您的第一个模板是多余的:内置模板规则会为您执行此操作。您的第二个模板基本上与 identity transform 模板做同样的事情。 IOW,你又回到了我在第一版回答中处理的情况。
    猜你喜欢
    • 2016-11-25
    • 1970-01-01
    • 2011-07-16
    • 1970-01-01
    • 2014-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多