如何从 XML 元素中删除一个特定的命名空间

Posted

技术标签:

【中文标题】如何从 XML 元素中删除一个特定的命名空间【英文标题】:How to remove one specific namespace from XML element 【发布时间】:2018-12-01 20:00:41 【问题描述】:

我已经寻找过这个具体案例,但没有找到。我有这个 XML,除了这三个元素之外还有其他元素,但它们无关紧要。相关元素附有两个命名空间。我想删除 xmlns:two 所以在输出中只有第一个 xmlns 存在。

<?xml version="1.0" encoding="UTF-8"?>
<Header>
    <one:id xmlns:one="http://x.com/xsd/so" 
        xmlns:two="http://x.com/xsd/woa.xsd">555</one:id>
    <one:protocolVersion xmlns:one="http://x.com/xsd/so" 
        xmlns:two="http://x.com/xsd/woa.xsd">2.0</one:protocolVersion>
    <one:userId xmlns:one="http://x.com/xsd/so" 
        xmlns:two="http://x.com/xsd/woa.xsd">12345</one:userId>
</Header>

现在我想要的是删除 xmlns:two

<?xml version="1.0" encoding="UTF-8"?>
<Header>
    <one:id xmlns:one="http://x.com/xsd/so">555</one:id>
    <one:protocolVersion xmlns:one="http://x.com/xsd/so">2.0</one:protocolVersion>
    <one:userId xmlns:one="http://x.com/xsd/so">12345</one:userId>
</Header>

我尝试过类似的方法,但它删除了错误的命名空间。它删除了作为前缀的相同命名空间。

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:two="http://x.com/xsd/woa.xsd" 
    xmlns:one="http://x.com/xsd/so">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="one:id">
        <xsl:element name="local-name()">
            <xsl:copy-of select="namespace::*[not(. = namespace-uri(..))]"/>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

【问题讨论】:

请注意,您的 XML 格式不正确。您有一个开始的&lt;one:id&gt; 标记,但有一个结束的&lt;/xro:id&gt; 标记。 谢谢@TimC,我修好了。 【参考方案1】:

有很多事情会阻止它产生您所描述的结果:

在这里,您只复制了元素使用的命名空间,而不是它使用的命名空间:

<xsl:copy-of select="namespace::*[not(. = namespace-uri(..))]"/>

在这里,您在 null 命名空间(无命名空间)中创建一个元素:

<xsl:element name="local-name()">

您已在 XSLT 样式表的根元素上声明了 onetwo 命名空间,这意味着它们将包含在输出文档的根元素中。

为了得到你描述的结果,你可以使用这个:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="one:*" xmlns:one="http://x.com/xsd/so" >
        <xsl:element name="name()" namespace="namespace-uri()">
            <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

或者您可以使用更通用、与命名空间无关的方法:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="*">
        <xsl:element name="name()" namespace="namespace-uri()">
            <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

这两者都会产生你所说的你想要的输出。

【讨论】:

【参考方案2】:

你想要的xsl:copy-of表达式就是这个,只复制与当前元素匹配的命名空间

<xsl:copy-of select="namespace::*[. = namespace-uri(..)]"/>

此外,您应该使用name() 而不是local-name() 创建元素,否则您将在没有命名空间中创建元素,因为您没有默认命名空间。

但是,由于您正在创建一个带有已在 XSLT 中声明的前缀的新元素,我认为您根本不需要复制名称空间。

试试这个 XSLT

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:one="http://x.com/xsd/so">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

    <xsl:template match="one:*">
        <xsl:element name="name()">
            <xsl:apply-templates select="node()|@*"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

注意,如果您可以使用 XSLT 2.0,您也可以这样做......

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:one="http://x.com/xsd/so">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

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

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

【讨论】:

事实上,您可以在顶层无条件地执行&lt;xsl:copy-of select="/" copy-namespaces="no"/&gt; - 这会删除所有未使用的命名空间,这些命名空间可能(或可能不)满足要求。无论如何,它适用于这个例子。

以上是关于如何从 XML 元素中删除一个特定的命名空间的主要内容,如果未能解决你的问题,请参考以下文章

xquery 转换在元素中创建空命名空间

Linq to Xml:删除命名空间

如何使用 XSLT 从 XML 中删除名称空间

如何从杰克逊 XML 解析中删除命名空间定义

如何从输出 xml 中删除命名空间?

如何使用 java dom 从 xml 中删除命名空间?