XSLT-преобразование с динамическим пространством имен - PullRequest
3 голосов
/ 27 июля 2010

Мне нужно преобразовать файл XML в другой файл XML, где исходный файл имеет динамическое пространство имен, установленное на xmlns="whatever".Мой XSLT работает нормально без пространства имен в файле, но я не получаю вывод с пространством имен.Как я могу заставить схему исходного файла быть примененной к файлу назначения?

Вся помощь приветствуется и спасибо заранее!

РЕДАКТИРОВАТЬ:

Я пытаюсь скопировать пространство имен uri в получившийся файл:

<xsl:param name="schema">
    <xsl:value-of select="namespace-uri()" />
</xsl:param>

<xsl:element name="root" namespace="$schema">

Я проверил, что схема содержит правильное значение, но проблема в том, что программа, кажется, принимает это тожебуквально:

<root xmlns="$schema">

Это правильный путь?

РЕДАКТИРОВАТЬ x2:

Я реализовал предложение Алехандро о:

<xsl:element name="root" namespace="{$schema}"/> 

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

<root xmlns="NAMESPACE">
    <foo xmlns="">

и т. д.

Есть ли способ заключить все элементы в это пространство имен, кроме добавления namespace={$schema} в каждую строку?Получи награду за лучший ответ!

РЕДАКТИРОВАТЬ x3: Лучший пример:

Если я сделаю:

<xsl:element name="root" namespace="{namespace-uri()}>
  <xsl:element name="foo">
    <xsl:element name="bar">
    <!--etc-->
    </xsl:element>
  </xsl:element>
</xsl:element>

Я получу:

<root xmlns="NAMESPACE">
  <foo xmlns="">
    <bar>
    <!--etc-->
    </bar>
  </foo>
<root>

Я бы хотел, чтобы они все были в пространстве имен NAMESPACE, поэтому я сделал:

<xsl:element name="root" namespace="{namespace-uri()}>
  <xsl:element name="foo" namespace="{namespace-uri()}>
    <xsl:element name="bar" namespace="{namespace-uri()}>
    <!--etc-->
    </xsl:element>
  </xsl:element>
</xsl:element>

Однако это некрасиво и утомительно печатать.Есть ли более простой способ перекрыть пространство имен всеми элементами?(надеюсь, это проясняет, что мне нужно)

Ответы [ 3 ]

4 голосов
/ 28 июля 2010

Предположим, у вас есть этот XML-ввод:

<root xmlns="survivors"> 
  <louis/> 
  <francis/> 
</root> 

Это означает, что каждый элемент находится под пространством имен по умолчанию, для которого его URI является "оставшимися в живых".

Как пишет Welbog, вы можете выбрать francisэлемент с:

/*/*[local-name()='francis']

или

/*[local-name()='root']/*[local-name()='francis']

Но, который также выбирает элемент francis из следующих XML-входов:

<root xmlns="survivors" xmlns:n="no-survivors"> 
  <louis/> 
  <n:francis/> 
</root> 

или

<root xmlns="survivors"> 
  <louis/> 
  <francis xmlns="no-survivors"/> 
</root> 

Вы также можете усилить предикат с помощью некоторого URI пространства имен.Но кто?Параметром может быть пространство имен по умолчанию для корневого элемента, например:

/*/*[local-name()='francis'][namespace-uri()=namespace-uri(/*)]

Конечно, это делает выражение XPath очень многословным.

В XSLT 2.0 вы можете использовать атрибут xsl:xpath-default-namespace, например:

<xsl:value-of select="/root/francis" xpath-default-namespace="survivors"/> 

Но это не очень хорошо для вашего случая, потому что вы не знаете URI заранее.

EDIT : xsl:element атрибуты AVT (шаблон значений атрибутов)) так что вам нужно это:

<xsl:element name="root" namespace="{$schema}"/> 

Также я рекомендую вам объявить параметр как строковый тип данных (не RTF, как сейчас), что-то вроде:

<xsl:param name="schema" select="namespace-uri()"/> 

РЕДАКТИРОВАТЬ 2 : Может быть, я не был ясен.Вам не нужно xsl:element/@namespace в каждом случае.После вашего заявления о том, что каждый элемент находится только в одном пространстве имен по умолчанию, эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[local-name()='bar']">
        <xsl:element name="newbar" namespace="{namespace-uri()}">
                <xsl:apply-templates select="@*|node()"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

С этим вводом:

<root xmlns="whatever">
    <foo/>
    <bar/>
</root>

Вывод:

<root xmlns="whatever">
    <foo></foo>
    <newbar></newbar>
</root>

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

<root xmlns="whatever">
    <foo/>
    <bar/>
</root>

В это:

<root xmlns="whatever">
    <foo>
        <bar/>
    </foo>
</root> 

Вы можете использовать эту таблицу стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:copy>
            <xsl:apply-templates select="*[1]|following-sibling::*[1]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
1 голос
/ 27 июля 2010

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

Допустим, у вас есть такой исходный файл:

<root xmlns="survivors">
  <louis/>
  <francis/>
</root>

Один из способов доступа к этим элементам с помощью XSLT - указать пространство имен, соответствующее источникуПространство имен файла по умолчанию:

<xsl:stylesheet
  version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:surv="survivors"
>
  <xsl:template match="surv:louis">
    <!-- ETC -->

Этот способ работает, когда вы знаете пространство имен.Когда вы не знаете пространство имен, вы можете игнорировать его, используя функцию XPath local-name(), например:

<xsl:stylesheet
  version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
  <xsl:template match="*[local-name() = 'louis']">
    <!-- ETC -->

Использование local-name() означает, что вы можете игнорировать пространство имен висходный документ.Вы должны быть осторожны, если в разных пространствах имен есть несколько элементов с одинаковым локальным именем.Это не совсем надежное решение, но если вы не можете доверять своему пространству имен, у вас все равно не так много опций.

Я бы предположил, что наличие переменного пространства имен - это большая проблема сама по себе.,Если это под вашим контролем, вы должны исправить это.Если это не под вашим контролем, вы должны нажать, чтобы исправить это.

0 голосов
/ 28 июля 2010

XSLT редко выполняется в вакцине. Это почти всегда часть какого-то другого приложения, которое загружает XSLT, загружает исходный документ и затем выводит результат.

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

Это сделает ваш XSLT намного более естественным для написания и поддержки. Я профессионал в XSLT и постоянно им пользуюсь, и вот как бы я решил это. Я не мог представить безобразие таблицы стилей, которая должна постоянно использовать local-name() сравнения. Какая боль. (Конечно, в гораздо более сложных сценариях выбора может и не быть. К счастью, ваш не один из них.)

Если у вас нет этой опции, я сочувствую.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...