Главная » XSLT » Переименование элементов или атрибутов

0

Задача

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

Решение

Если нужно переименовать лишь небольшое количество элементов или атри­бутов, воспользуйтесь прямолинейным вариантом идиомы переопределяемого копирования, как показано в примере 8.5.

Пример 8.5. Переименовать элемент person в individual

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="copy.xslt"/>

<xsl:output method="xml" version="1.0" encoding="UTF-8"/>

<xsl:template match="person"> <individual>

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

</xsl:stylesheet>

Или по-другому, с использованием xsl:element:

<xsl:template match="person">

<xsl:element name="individual">

<xsl:apply-templates/> </xsl:element> </xsl:template>

Переименовать атрибуты столь же просто:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="copy.xslt"/>

<xsl:output method="xml" version="1.0" encoding="UTF-8"/>

<xsl:template match="@lastname"> <xsl:attribute name="surname">

<xsl:value-of select="."/> </xsl:attribute> </xsl:template>

Иногда нужно изменить имя пространства имен, а не локальное имя. См. при­мер 8.6.

Пример 8.6. В этом документе используется пространство имен foo

<foo:someElement xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo"> <foo:aChild>

<foo:aGrandChild/> <foo:aGrandChild> </foo:aGrandChild> </foo:aChild> </foo:someElement>

Для каждого элемента в пространстве имен foo создадим новый элемент в пространстве имен bar, как показано в примерах 8.7 и 8.8.

Пример 8.7. Таблица стилей для замените на bar

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo"

xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">

<xsl:import href="copy.xslt"/>

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:template match="foo:*">

<xsl:element name="bar:{local-name()}">

<xsl:apply-templates/> </xsl:element> </xsl:template>

</xsl:stylesheet>

Пример 8.8. Результат

<bar:someElement xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar"> <bar:aChild>

<bar:aGrandChild/> <bar:aGrandChild/> </bar:aChild> </bar:someElement>

Обсуждение

Придумывание имен – это важное умение, которым владеют немногие прак­тикующие программисты (ваш покорный слуга к их числу не принадлежит)1. По­этому нужно знать, как изменить имя, оказавшееся неудачным.

Если нужно переименовать сразу много элементов или атрибутов, то имеет смысл прибегнуть к табличному решению, как показано в примерах 8.9 – 8.11.

Пример 8.9. Обобщенная таблица стилей для переименования, управляе­мая таблицей данных

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">

<xsl:import href="copy.xslt"/>

<!– Переопределить в импортирующей таблице –> <xsl:variable name="lookup" select="/.."/>

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="*"> <xsl:choose>

<xsl:when test="$lookup/ren:element[@from=name(current())]"> <xsl:element

name="{$lookup/ren:element[@from=local-name(current())]/@to}"> <xsl:apply-templates select="@*"/> <xsl:apply-templates/> </xsl:element> </xsl:when> <xsl:otherwise>

<xsl:apply-imports/> </xsl:otherwise> </xsl:choose> </xsl:template>

<xsl:template match="@*"> <xsl:choose>

<xsl:when test="$lookup/ren:attribute[@from=name(current())]">

<xsl:attribute name="{$lookup/ren:attribute[@from=name(current())]/@to}"> <xsl:value-of select="."/>

</xsl:attribute> </xsl:when> <xsl:otherwise>

<xsl:apply-imports/> </xsl:otherwise> </xsl:choose> </xsl:template>

</xsl:stylesheet>

Пример 8.10. Применение показанной выше таблицы стилей

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">

<xsl:import href="TableDrivenRename.xslt"/>

<!– Загружаем справочную таблицу. Мы определили ее локально, но можно было взять из внешнего файла –>

<xsl:variable name="lookup" select="document(”)/*[ren:*]"/>

<!– Определяем правила переменования –> <ren:element from="person" to="individual"/> <ren:attribute from="firstname" to="givenname"/> <ren:attribute from="lastname" to="surname"/> <ren:attribute from="age" to="yearsOld"/>

</xsl:stylesheet>

Пример 8.11. Результат

<?xml version="1.0" encoding="UTF-8"?> <people which="MeAndMyFriends">

<individual givenname="Sal" surname="Mangano" yearsOld="38" height="5.75"/>

<individual givenname="Mike" surname="Palmieri" yearsOld="28" height="5.10"/>

<individual givenname="Vito" surname="Palmieri" yearsOld="38" height="6.0"/>

<individual givenname="Vinny" surname="Mari" yearsOld="37" height="5.8"/>

</people>

Этот подход применим и тогда, когда некоторые элементы или атрибуты нуж­но обрабатывать с учетом контекста. Рассмотрим, например, такой фрагмент до­кумента:

<clubs>

<club name="The 500 Club"> <members>

<member name="Joe Smith">

<position name="president"/> </member>

<member name="Jill McFonald">

<position name="treasurer"/> </member> <!– … –> <members> </club> <!– … –> <clubs>

Предположим, что требуется переименовать атрибут @name в @title, но только для элементов position. Если воспользоваться табличным решением, то атрибут @name будет изменен во всех элементах. Чтобы выйти из положения, со­здадим шаблон, который переопределяет поведение по умолчанию для всех эле­ментов, кроме position.

<xsl:stylesheet version="1.0"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ren="http://www.ora.com/namespaces/rename">

<xsl:import href="TableDrivenRename.xslt"/>

<!– Загружаем справочную таблицу. Мы определили ее локально, но можно было взять из внешнего файла –>

<xsl:variable name="lookup" select="document(”)/*[ren:*]"/>

<!– Определяем правила переменования –> <ren:attribute from="name" to="title"/>

<!– ПЕРЕОПРЕДЕЛЕНИЕ: все атрибуты, не принадлежащие элементу position, просто копируются — >

<xsl:template match="@name[not(parent::position)]">

<xsl:copy/> </xsl:template>

Если для переименования пространства имен применяется копирование, то старое пространство имен может упрямо отказываться исчезать, хотя оно больше и не нужно. Снова рассмотрим документ foo с дополнительным элементом из пространства имен doc:

<foo:someElement xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo" xmlns:doc="http://www.ora.com/XMLCookbook/namespaces/doc"> <foo:aChild>

<foo:aGrandChild/> <foo:aGrandChild>

<doc:doc>Эту документацию не следует ни удалять, ни изменять. </doc:doc> </foo:aGrandChild> </foo:aChild>

</foo:someElement>

Если применить к этому документу таблицу стилей для переименования про­странств имен, то к элементу doc окажется присоединено пространство имен foo.

<bar:someElement xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar"> <bar:aChild>

<bar:aGrandChild/> <bar:aGrandChild>

<doc:doc xmlns:doc="http://www.ora.com/XMLCookbook/namespaces/doc" xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo"> Эту документацию не следует ни удалять, ни изменять. </doc:doc> </bar:aGrandChild> </bar:aChild>

</bar:someElement>

Объясняется это тем, что элемент doc обрабатывается командой xsl:copy. И xsl:copy, и xsl:copy-of всегда копируют все пространства имен, ассоции­рованные с элементом. В XSLT 2.0 у обеих этих команд есть необязательный атри­бут copy-namespaces, который может принимать значения yes или no. По­скольку элемент doc является потомком элементов из пространства имен foo, то у него есть узел пространства имен foo, пусть даже во входном документе его не видно. Во избежание копирования нежелательного пространства имен воспользу­емся командой xsl:element, чтобы не копировать элементы, а создать их заново.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:foo="http://www.ora.com/XMLCookbook/namespaces/foo" xmlns:bar="http://www.ora.com/XMLCookbook/namespaces/bar">

<xsl:import href="copy.xslt"/>

<xsl:strip-space elements="*"/>

<!– Для каждого элемента создаем новый элемент с таким же локальным именем и пространством имен. –> <xsl:template match="*">

<xsl:element name="{name()}" namespace="{namespace-uri()}">

<xsl:apply-templates/> </xsl:element> </xsl:template>

<xsl:template match="foo:*">

<xsl:element name="bar:{local-name()}">

<xsl:apply-templates/> </xsl:element> </xsl:template>

</xsl:stylesheet>

Этот прием можно применить и для удаления всех пространств имен из документа:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:import href="copy.xslt"/>

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:template match="*">

<xsl:element name="{local-name()}">

<xsl:apply-templates/> </xsl:element> </xsl:template>

</xsl:stylesheet>

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии