Как устранить записи xmlns=, созданные XSLT-преобразованием одного XML-документа в другой XML-документ

Хорошо, я видел множество вариантов этого вопроса, но ни один из них точно не отвечает на то, что я пытаюсь решить, и, возможно, я просто слишком туп, чтобы понять, как применить один из других ответов к тому, что я пытаюсь сделать.

У меня есть XML, который выглядит примерно так:

<?xml version="1.0" encoding="utf-8"?>
<message>
  <cmd id="api_info">
    <api-version>1.0</api-version>
    <api-build>1.0.0.0</api-build>
  </cmd>
</message>

Теперь у меня есть преобразование XSLT, которое я применяю к этому XML. XSLT похож на следующий:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                version="2.0">

    <xsl:output method="xml" version="1.0" indent="yes"/>

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

    <xsl:template match="message">
        <xsl:element name="message" xmlns="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="/message/cmd/@id">
        <xsl:variable name="_commandType" select="/message/cmd/@id"/>
        <xsl:element name="messageHeader">
            <xsl:element name="cmdType">
                <xsl:value-of select="$_commandType"/>
            </xsl:element>
        </xsl:element>

        <xsl:element name="messageBody">
            <xsl:choose>
                <xsl:when test="$_commandType = 'api_info'">
                    <xsl:element name="apiInfoBody">
                        <xsl:element name="apiVersion">
                            <xsl:value-of select="/message/cmd/api-version"/>
                        </xsl:element>
                        <xsl:element name="apiBuild">
                            <xsl:value-of select="/message/cmd/api-build"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="$_commandType = 'communicationError'">
                    <xsl:element name="communicationErrorBody">
                        <xsl:element name="errorCode">
                            <xsl:value-of select="error-code"/>
                        </xsl:element>
                        <xsl:element name="badCmd">
                            <xsl:value-of select="bad-cmd"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Результат, который я получаю, в основном то, что я хочу, и выглядит следующим образом:

<?xml version="1.0" encoding="UTF-8"?>
<message xmlns="http://www.companyname.com/schemas/product/Version001">
    <messageHeader xmlns="">
        <cmdType>api_info</cmdType>
    </messageHeader>
    <messageBody xmlns="">
        <apiInfoBody>
            <apiVersion>1.0</apiVersion>
            <apiBuild>1.0.0.0</apiBuild>
        </apiInfoBody>
    </messageBody>
</message>

Но мне не нужны атрибуты xmlns="" в элементах ‹messageHeader› и ‹messageBody›.

Теперь я обнаружил, что если я явно указываю пространство имен в XSLT для этих элементов, то нежелательный атрибут перемещается на один уровень вниз к дочерним элементам этих атрибутов.

Я мог бы просто просмотреть весь свой XSLT и явно добавить xmlns=""http://www.companyname.com/schemas/product/Version001" каждому из моих определений xsl:element, но я знаю, что должен быть более элегантный способ. программисты слишком ленивы, чтобы не иметь ярлыка для такой чепухи. Если бы мой XSLT не состоял из чего-то столь же простого, как укороченный пример, у меня возникло бы искушение сделать это таким образом. Но я знаю, что должен быть лучший способ .

Кто-нибудь знает, что мне здесь не хватает?

Спасибо,

БудильникTripper


person AlarmTripper    schedule 04.12.2009    source источник


Ответы (3)


Используйте префиксы exclude-result-prefixs в теге xsl:stylesheet с префиксом "#default"

Ссылка в w3c для этого ЗДЕСЬ

РЕДАКТИРОВАТЬ: Хорошо, я должен был более внимательно изучить ваш XSL. Переместите xmlns в теге сообщения до тега таблицы стилей. Это поместит ВСЕ элементы результата в одно и то же пространство имен и приведет к одному атрибуту пространства имен в теге сообщения. Я запустил это в Oxygen/XML и получил желаемый результат.

person Jim Garrison    schedule 04.12.2009
comment
Не изменил выходной XML. - person AlarmTripper; 04.12.2009
comment
Какой процессор XSLT вы используете? - person Jim Garrison; 04.12.2009
comment
Да, я подтвердил, что ваше новое предложение также работает и, вероятно, является лучшим решением, чем мое, поскольку оно сохраняет назначение xmlns в элементе таблицы стилей. И опция #default избавляет меня от необходимости исключать каждый из других префиксов пространств имен. Большое спасибо. Мне нужно многое узнать об этом XSLT, и я думаю, что пространства имен, вероятно, являются одним из самых сложных аспектов этого. - person AlarmTripper; 04.12.2009
comment
Я использую Altova XMLSpy Professional. Только что купил его сегодня, чтобы помочь справиться с некоторыми из этих вещей. - person AlarmTripper; 04.12.2009
comment
Добро пожаловать в XSLT! Есть чему поучиться, но вы обнаружите, что это богатая среда (особенно XSLT 2). Если вы еще этого не сделали, получите копию справочника по XSLT/XPATH 2.0 Майкла Кея (ISBN 0470192747, 4-е издание, апрель 2008 г.). Я использую свою копию все время, и это не просто ссылка. Также ознакомьтесь со спецификацией XSLT на w3c.org. Это может быть пугающим, но его не так уж сложно прочитать, и это окончательное определение того, что такое XSLT. - person Jim Garrison; 04.12.2009
comment
О, и в книге Майкла Кея есть довольно обширное обсуждение пространств имен :-) - person Jim Garrison; 04.12.2009

Хорошо, я понял это.

XSLT, который я хочу, выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:fo="http://www.w3.org/1999/XSL/Format"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                exclude-result-prefixes="xsl fo xs fn">

    <xsl:output method="xml" version="1.0" indent="yes"/>

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

    <xsl:template match="message">
        <message namespace="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </message>
    </xsl:template>

    <xsl:template match="/message/cmd/@id">
        <xsl:variable name="_commandType" select="/message/cmd/@id"/>

        <xsl:element name="messageHeader">
            <xsl:element name="cmdType">
                <xsl:value-of select="$_commandType"/>
            </xsl:element>
        </xsl:element>

        <xsl:element name="messageBody">
            <xsl:choose>
                <xsl:when test="$_commandType = 'api_info'">
                    <xsl:element name="apiInfoBody">
                        <xsl:element name="apiVersion">
                            <xsl:value-of select="/message/cmd/api-version"/>
                        </xsl:element>
                        <xsl:element name="apiBuild">
                            <xsl:value-of select="/message/cmd/api-build"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
                <xsl:when test="$_commandType = 'communicationError'">
                    <xsl:element name="communicationErrorBody">
                        <xsl:element name="errorCode">
                            <xsl:value-of select="error-code"/>
                        </xsl:element>
                        <xsl:element name="badCmd">
                            <xsl:value-of select="bad-cmd"/>
                        </xsl:element>
                    </xsl:element>
                </xsl:when>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Исправление проблемы заключалось в добавлении атрибута exclude-result-prefixes к элементу ‹xsl:stylesheet› и изменении раздела, который был следующим:

    <xsl:template match="message">
        <xsl:element name="message" xmlns="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </xsl:element>
    </xsl:template>

вместо этого быть следующим:

    <xsl:template match="message">
        <message namespace="http://www.companyname.com/schemas/product/Version001">
            <xsl:apply-templates select="/message/cmd/@id"/>
        </message>
    </xsl:template>

И теперь я снова счастлив.

Вероятно, есть лучшие способы сделать это, но это работает для меня. Любые будущие предложения по-прежнему приветствуются.

person AlarmTripper    schedule 04.12.2009

Ответ Джима Гаррисона (обновленный) - это все, что вам нужно. Но опубликованная вами обновленная таблица стилей выводит в результате атрибут «пространство имен», поэтому я не уверен, что это вам даст.

Главное, что нужно понять, это то, как работает пространство имен по умолчанию. Не думайте о xmlns как об атрибуте, который вы можете вывести в результат. Вместо этого думайте об этом как о лексической детали вашей таблицы стилей, целью которой является установка пространства имен по умолчанию для всего, что находится под ним (самого элемента и каждого из его потомков, пока он не будет переопределен). (В свою очередь, он имеет ту же функцию в результирующем XML, структура которого сильно отличается от самой таблицы стилей.)

Буквенные элементы результата в вашей таблице стилей (например, ‹message>), а также инструкции ‹xsl:element> (например, ‹xsl:element name="message">) принимают пространство имен по умолчанию, когда предоставленное имя не использует префикс. Если вы хотите, чтобы все использовали одно и то же пространство имен, вам нужно поместить пространство имен по умолчанию вверху таблицы стилей, как предложил Джим Гаррисон.

В противном случае вы получите некоторые элементы, которых нет в этом пространстве имен, поэтому результат содержит xmlns="" (удаление пространства имен для этих элементов).

person Evan Lenz    schedule 07.12.2009
comment
Спасибо, Эван! Я ценю всю помощь, которую я могу получить здесь. Я читал, что «xmlns» выглядит как атрибут, но это не так. Я вернулся и изменил код, чтобы он соответствовал тому, что предложил Джим, а также добавил квалификатор пространства имен (терминологию?), так что мой тег пространства имен теперь находится в моем определении «таблицы стилей» и выглядит как «xmlns:grf=blahblah.com, а затем вернулся, чтобы убедиться, что мои элементы имеют префикс grf:. Помощь от вас, экспертов здесь, будет неоценимой, поскольку никто в моей компании не сделал ничего особенного с XSLT или XPATH. Хотя мне очень нравится учиться! - person AlarmTripper; 08.12.2009