Как удалить пространства имен из XML с помощью XSLT

У меня есть файл размером 150 Мбайт (это может быть еще больше) XML файл. Мне нужно удалить все пространства имен. Это на Visual Basic 6.0, поэтому я использую DOM для загрузки XML. Загрузка в порядке, сначала я был настроен скептически, но как-то эта часть отлично работает.

Я пытаюсь использовать XSLT, но также удаляет все остальные атрибуты. Я хочу сохранить все атрибуты и элементы, мне просто нужно удалить пространства имен. По-видимому, это потому, что у меня есть xsl:element, но не атрибут. Как включить атрибуты?

<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>

Ответы

Ответ 1

Ваш XSLT также удаляет атрибуты, потому что у вас нет шаблона, который бы их копировал. <xsl:template match="*"> соответствует только элементам, а не атрибутам (или тексту, комментариям или инструкциям по обработке).

Ниже приведена таблица стилей, которая удаляет все определения пространства имен из обработанного документа, но копирует все остальные узлы и значения: элементы, атрибуты, комментарии, текст и инструкции по обработке. Пожалуйста, обратите внимание на 2 вещи

  • Копирование атрибутов как таковых недостаточно для удаления всех пространств имен. Также атрибут может принадлежать пространству имен, даже если содержащийся элемент не принадлежит пространству имен. Поэтому также необходимо создавать атрибуты, подобные элементам. Создание атрибутов выполняется с помощью элемента <xsl:attribute>.
  • Действительный документ XML не может содержать элемент с двумя или более атрибутами с таким же расширенным именем, но элементы могут содержать несколько атрибутов с одним и тем же локальным именем , если атрибуты имеют разные пространства имен. Это означает, что удаление префикс пространства имен из имени атрибута вызовет dataloss, если есть элемент, который имеет два атрибута с одним и тем же локальным именем. Другой из этих атрибутов будет удален (или перезаписан).

... и код:

<?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, чтобы предотвратить соответствие элементов этому шаблону.

Ответ 2

Как включить туда атрибуты?

Просто добавьте этот шаблон к тому, который у вас уже есть:

<xsl:template match="@*">
    <xsl:copy/>
</xsl:template>

Ответ 3

<?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>