Преобразование идентичности XSLT создает неправильное пространство имен - PullRequest
2 голосов
/ 08 мая 2011

У меня есть нестандартная XML-схема, которая развивается: элементы добавляются, другие удаляются, а пространство имен изменяется в соответствии с новой версией (например, с "http://foo/1.0" на" http://foo/1.1"). Я хочу написать XSLT, который преобразуетXML-документы из старой схемы в новую. Моя первая попытка работает, но она многословна и не масштабируется, и мне нужна помощь в ее уточнении.

Вот пример документа для схемы 1.0:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo:rootElement xmlns:foo="http://foo/1.0">
    <obsolete>something</obsolete>
    <stuff>
        <alpha>a</alpha>
        <beta>b</beta>
    </stuff>
</foo:rootElement>

В схеме 1.1 устаревший элемент исчезает, но все остальное остается, поэтому XSLT нужно сделать две вещи:

  1. Удалить тег
  2. Изменить пространство именот http://foo/1.0 до http://foo/1.1

Вот мое решение:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:foo1="http://foo/1.0"
    xmlns:foo="http://foo/1.1" 
    exclude-result-prefixes="foo1">

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

    <!-- Remove the "obsolete" tag -->    
    <xsl:template match="foo1:rootElement/obsolete"/>

    <!-- Copy the "alpha" tag -->
    <xsl:template match="foo1:rootElement/stuff/alpha">
        <alpha>
            <xsl:apply-templates/>
        </alpha>
    </xsl:template>

    <!-- Copy the "beta" tag -->
    <xsl:template match="foo1:rootElement/stuff/beta">
        <beta>
            <xsl:apply-templates/>
        </beta>
    </xsl:template>

    <!-- Copy the "stuff" tag -->
    <xsl:template match="foo1:rootElement/stuff">
        <stuff>
            <xsl:apply-templates/>
        </stuff>
    </xsl:template>

    <!-- Copy the "rootElement" tag -->
    <xsl:template match="foo1:rootElement">
        <foo:rootElement>
            <xsl:apply-templates/>
        </foo:rootElement>
    </xsl:template>

</xsl:stylesheet>

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

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

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:foo1="http://foo/1.0"
    xmlns:foo="http://foo/1.1" 
    exclude-result-prefixes="foo1">

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

    <xsl:template match="foo1:rootElement/obsolete"/>

    <xsl:template match="node()|@*" name="identity">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="foo1:rootElement/stuff">
        <xsl:copy>
            <xsl:call-template name="identity"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="foo1:rootElement">
        <foo:rootElement>
            <xsl:apply-templates/>
        </foo:rootElement>
    </xsl:template>

</xsl:stylesheet>

Но он выдает:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<foo:rootElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:foo="http://foo/1.1">  
    <stuff xmlns:foo="http://foo/1.0">
      <stuff>
        <alpha>a</alpha>
        <beta>b</beta>
      </stuff>
   </stuff>
</foo:rootElement>

Элемент "материал" был скопирован, что я хочу, но он обернут вдругой элемент «вещи», помеченный старым пространством имен.Я не понимаю, почему это происходит.Я пробовал много способов исправить это, но безуспешно.Какие-либо предложения?Спасибо.

Ответы [ 2 ]

2 голосов
/ 09 мая 2011

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pOldNS" select="'http://foo/1.0'"/>
 <xsl:param name="pNewNS" select="'http://foo/1.1'"/>

 <xsl:template match="/*">
  <xsl:element name="{name()}" namespace="{$pNewNS}">
   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="*">
  <xsl:element name="{name()}">
   <xsl:copy-of select="namespace::*[not(.=$pOldNS)]"/>

   <xsl:apply-templates select="node()|@*"/>
  </xsl:element>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:choose>
   <xsl:when test="namespace-uri()=$pOldNS">
    <xsl:attribute name="{name()}" namespace="{$pNewNS}">
     <xsl:value-of select="."/>
    </xsl:attribute>
   </xsl:when>
   <xsl:otherwise>
    <xsl:copy-of select="."/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="obsolete"/>
</xsl:stylesheet>

при применении к этому документу XML :

<foo:rootElement xmlns:foo="http://foo/1.0">
    <obsolete>something</obsolete>
    <stuff>
        <alpha x="y" foo:t="z">a</alpha>
        <beta>b</beta>
    </stuff>
</foo:rootElement>

дает требуемый, правильный результат (obsolute элементы удалены, пространство имен обновлено, правильно обработаны узлы атрибутов, включая узлы, которые были в старом пространстве имен):

<foo:rootElement xmlns:foo="http://foo/1.1">
   <stuff>
      <alpha x="y" foo:t="z">a</alpha>
      <beta>b</beta>
   </stuff>
</foo:rootElement>

Основные преимущества этого решения :

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

  2. General, позволяет передавать старое и новое пространство имен в качестве внешних параметров преобразованию.

2 голосов
/ 09 мая 2011

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

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

    <xsl:template match="foo1:rootElement/obsolete"/>

    <xsl:template match="attribute()">
      <xsl:copy/>
    </xsl:template>

    <xsl:template match="element()">
      <xsl:variable name="name" select="local-name()"/>
      <xsl:element name="{$name}" namespace="http://foo/1.1">
        <xsl:apply-templates select="node()|@*"/>
      </xsl:element>
    </xsl:template>

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