Объединить атрибуты xmlns в корневой элемент?

Я конвертирую набор XML-документов из одного формата, который не включает префиксы пространств имен, в другой, который включает. Все относительно просто, но в выводе XMLNS это немного повторяется. Ниже приведен пример.

(Очень просто) Ввод XML

<?xml version="1.0"?>
<a/>

XSLT

<!-- xmlns="http://www.w3.org/1999/xhtml" -->

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"

    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd"

    version="1.0">

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

    <xsl:template match="/a">
        <xsl:element name="a:test-a">
            <!-- attempted fix -->
            <xsl:copy-of select="namespace::*"/>

            <!-- is there a better way to get this in there? -->
            <xsl:attribute name="xsi:schemaLocation">urn:local.test schema/test.xsd</xsl:attribute>

            <xsl:element name="b:test-b">
                <xsl:element name="c:test-c">content</xsl:element>
            </xsl:element>

        </xsl:element>
    </xsl:template>

</xsl:stylesheet>

Вывод

<?xml version="1.0"?>
<a:test-a
    xmlns:a="urn:data.test-a"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
  <b:test-b xmlns:b="urn:data.test-b">
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
  </b:test-b>
  <b:test-b xmlns:b="urn:data.test-b">
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
    <c:test-c xmlns:c="urn:data.test-c">content</c:test-c>
  </b:test-b>
</a:test-a>

Желаемый результат

<?xml version="1.0"?>
<a:test-a
    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
  <b:test-b>
    <c:test-c>content</c:test-c>
    <c:test-c>content</c:test-c>
  </b:test-b>
  <b:test-b>
    <c:test-c>content</c:test-c>
    <c:test-c>content</c:test-c>
  </b:test-b>
</a:test-a>

По сути, я хочу объединить атрибуты пространства имен в корневой элемент. Я провел некоторое исследование и подумал, что это заблокировано, используя, как показано, 0__copying_namespace_nodes_using_the_namespace_axis" rel="nofollow">здесь . Но это не дает желаемого эффекта; Я предполагаю, что либо использую его неправильно, либо ограничен возможностями xsltproc.

Выполнение второго прохода для очистки записей XMLNS также будет прекрасным решением.

Кроме того, если это ограничивает решение, я думаю, что моя среда будет ограничена XSLT 1.0.

Спасибо за любые советы.

PS. Меньший вопрос: есть ли лучший способ получить этот атрибут schemaLocation в выходных данных, но это второстепенно.


person fracai    schedule 30.10.2010    source источник
comment
Хороший вопрос, +1. В моем ответе вы найдете краткое и полное решение в лучшем стиле письма, а также рекомендации.   -  person Dimitre Novatchev    schedule 30.10.2010


Ответы (1)


Это, вероятно, самое короткое преобразование, отвечающее вашим требованиям:

<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

    xmlns:a="urn:data.test-a"
    xmlns:b="urn:data.test-b"
    xmlns:c="urn:data.test-c"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:local.test schema/test.xsd">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="a">
        <a:test-a xmlns:a="urn:data.test-a"
           xmlns:b="urn:data.test-b"
           xmlns:c="urn:data.test-c"
           xsi:schemaLocation="urn:local.test schema/test.xsd">
            <b:test-b>
                <c:test-c>content</c:test-c>
            </b:test-b>
        </a:test-a>
    </xsl:template>
</xsl:stylesheet>

когда это преобразование выполняется в предоставленном XML-документе:

<a/>

Получен желаемый правильный результат:

<a:test-a xmlns:a="urn:data.test-a"
   xmlns:b="urn:data.test-b"
   xmlns:c="urn:data.test-c"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="urn:local.test schema/test.xsd">
   <b:test-b>
      <c:test-c>content</c:test-c>
   </b:test-b>
</a:test-a>

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

person Dimitre Novatchev    schedule 30.10.2010
comment
Ах, так что вы просто вставляете их напрямую. Спасибо за совет. Я понимаю проблемы с памятью. На этом этапе цель состоит в том, чтобы отразить образцы файлов. Когда он будет готов к развертыванию, будет достаточно легко переключиться на более эффективный, хотя и более подробный вариант. - person fracai; 30.10.2010
comment
все узлы пространства имен копируются во все дочерние элементы - поскольку спецификация не определяет реализацию, только семантику, не правда ли, что процессору нужно только вести себя , как если бы все узлы пространства имен копируются для всех дочерние элементы? Или, выражаясь иначе, все узлы-потомки имеют все узлы пространства имен предков (если они не затенены), но это не означает, что в реализации процессора они обязательно должны быть отдельными копиями? - person LarsH; 01.11.2010
comment
@LarsH: узлы пространства имен принадлежат всем дочерним узлам и на практике из соображений эффективности они также копируются. Вы можете увидеть, сколько там узлов пространства имен, просто подсчитав их. Этот результат ясно показывает, что существует больше узлов пространства имен, когда все пространства имен находятся в верхнем элементе, чем когда пространства имен расположены там, где они действительно необходимы. - person Dimitre Novatchev; 01.11.2010
comment
count() должен показывать результат, как будто узлы пространства имен находятся на всех потомках, потому что в спецификации семантически сказано, что они там; но это не означает, что конкретный процессор XSLT реализует это, создавая физические копии структур данных узла пространства имен. Я готов поверить вам на слово, что большинство из них делают, но кажется странным, что они сделали бы это из соображений эффективности, если стоимость памяти настолько высока, O (n) ... в то время как временные затраты на поиск предка ось - O (log (n)) ... или лучше, если у вас есть умная реализация, которая может копировать узлы пространства имен по запросу. - person LarsH; 01.11.2010
comment
@LarsH: Разработчики, вероятно, попробовали оба пути и выбрали более эффективную реализацию :) - person Dimitre Novatchev; 01.11.2010
comment
Он создает атрибут корневого узла xmlns = w3.org/2000/xmlns в результирующем XML. . Что мне делать, чтобы в результирующем XML было xmlns = MY_CUSTOM_URL? - person Neil; 29.08.2012
comment
Нет, со всеми моими 9 разными XSLT-процессорами результат такой же, как и в этом ответе. Вы должны использовать какой-то несовместимый / глючный XSLT-процессор. - person Dimitre Novatchev; 29.08.2012