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

26

我有一个150 MB(有时可能更大)的XML文件,需要删除所有命名空间。

由于我在使用Visual Basic 6.0,所以我正在使用DOM来加载XML。加载没问题,一开始我有些怀疑,但不知何故那部分正常工作了。

我正在尝试以下XSLT,但它也会删除所有其他属性。我想保留所有属性和元素,只需要删除命名空间。显然是因为我有xsl:element但没有attribute。如何在其中包含属性?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="UTF-8" />
    <xsl:template match="*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

可能是重复的问题:如何使用C#从XML中删除所有命名空间? - Peter Mortensen
3个回答

38
您的XSLT也会删除属性,因为您没有一个可以复制它们的模板。"<xsl:template match="*">"只能匹配元素,而不能匹配属性(或文本、注释或处理指令)。
下面是一个样式表,它从处理后的文档中删除了所有命名空间定义,但复制了所有其他节点和值:元素、属性、注释、文本和处理指令。请注意以下两点:
  1. 仅复制属性是不足以删除所有命名空间的。即使包含该属性的元素不属于命名空间,属性也可以属于命名空间。因此,像元素一样需要创建属性。创建属性使用 "<xsl:attribute>" 元素。
  2. 有效的 XML 文档不能包含具有相同扩展名称的两个或多个属性的元素,但元素可以包含具有相同本地名称的多个属性,如果这些属性具有不同的命名空间。这意味着从属性名称中删除命名空间前缀将导致数据丢失,如果有至少两个具有相同本地名称的属性的元素,则其中一个属性将被删除(或覆盖)。
...下面是代码:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes" method="xml" encoding="utf-8" omit-xml-declaration="yes"/>

    <!-- Stylesheet to remove all namespaces from a document -->
    <!-- NOTE: this will lead to attribute name clash, if an element contains
        two attributes with same local name but different namespace prefix -->
    <!-- Nodes that cannot have a namespace are copied as such -->

    <!-- template to copy elements -->
    <xsl:template match="*">
        <xsl:element name="{local-name()}">
            <xsl:apply-templates select="@* | node()"/>
        </xsl:element>
    </xsl:template>

    <!-- template to copy attributes -->
    <xsl:template match="@*">
        <xsl:attribute name="{local-name()}">
            <xsl:value-of select="."/>
        </xsl:attribute>
    </xsl:template>

    <!-- template to copy the rest of the nodes -->
    <xsl:template match="comment() | text() | processing-instruction()">
        <xsl:copy/>
    </xsl:template>

</xsl:stylesheet>

你也可以使用 <xsl:template match="node()"> 代替最后一个模板,但是这种情况下你应该使用 priority 属性来防止元素匹配到这个模板。


太棒了伙计,谢谢你。网上有很多其他的方案,但这是唯一一个有效的方案。 - speedinfusion
我在10年前点赞过这个.. 今天又使用了它。使用像这样的适当解析器比从命令行使用sed要好得多。 - Paulb

1
如何在那里包含属性?
只需将此模板附加到您已经拥有的模板中即可:
<xsl:template match="@*">
    <xsl:copy/>
</xsl:template>

-1
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/">
    <xsl:copy>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>
<xsl:template match="@*">
    <xsl:attribute name="{local-name()}">
        <xsl:value-of select="current()"/>
    </xsl:attribute>
</xsl:template>
<xsl:template match="*">
    <xsl:element name="{local-name()}">
        <xsl:apply-templates select="@* | * | text()"/>
    </xsl:element>
</xsl:template>
<xsl:template match="text()">
    <xsl:copy>
        <xsl:value-of select="current()"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

2
如果您能在代码周围添加一些解释,那就太好了。 - zx485

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