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

0

Задача

Имеется документ, в котором информация представлена в виде дочерних эле­ментов, а хотелось бы закодировать ее в атрибутах.

Решение

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

<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"> <xsl:copy>

<xsl:for-each select="*">

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

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

</xsl:stylesheet>

Обсуждение XSLT 1.0

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

<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">

<xsl:copy>

<xsl:for-each select="*">

<xsl:attribute name="{local-name(.)}"> <xsl:value-of select="."/> </xsl:attribute> <xsl:copy-of select="@*"/> </xsl:for-each> </xsl:copy> </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"/>

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

<xsl:for-each select="*"> <xsl:attribute name="{local-name(.)}">

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

<xsl:variable name="elem-name" select="local-name(.)"/> <xsl:for-each select="@*">

<xsl:attribute name="{concat($elem-name,’-‘,local-name(.))}">

<xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> </xsl:for-each> </xsl:copy> </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" indent="yes" version="1.0" encoding="UTF-8"/>

<!– Отобрать элементы, которые являются родителями –> <xsl:template match="*[*]"> <xsl:choose>

<!— Преобразовывать дочерние элементы, только если у этого элемента —> <!– нет атрибутов –> <xsl:when test="not(@*)"> <xsl:copy>

<!– Преобразовывать дочерний элемент в атрибут, если –> <!– у него нет ни потомков, ни атрибутов, а имена всех –> <!– братьев уникальны –> <xsl:for-each select="*"> <xsl:choose>

<xsl:when test="not(*) and not(@*) and

not(preceding-sibling::*[name() =

name(current())])

and

not(following-sibling::*[name() =

name(current())])">

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

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

<xsl:apply-templates select="."/> </xsl:otherwise> </xsl:choose> </xsl:for-each> </xsl:copy> </xsl:when> <xsl:otherwise> <xsl:copy>

<xsl:apply-templates/> </xsl:copy> </xsl:otherwise> </xsl:choose> </xsl:template>

В примере ниже мы несколько упростили и ускорили решения для XSLT 1.0, воспользовавшись командой xsl:for-each-group. Хитрость в том, что мы задали атрибут group-by="name( )" для выяснения того, есть ли братья с одинаковыми именами. Дополнительно в этом решении атрибуты преобразуе­мого элемента передаются родителю, что можно было бы сделать и для версии 1.0:

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

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

<!– Отобрать элементы, которые являются родителями –> <xsl:template match="*[*]"> <xsl:choose>

<!— Преобразовывать дочерние элементы, только если у этого элемента —> <!—нет атрибутов –> <xsl:when test="not(@*)"> <xsl:copy>

<!– Преобразовывать дочерний элемент в атрибут, если –> <!– у него нет ни потомков, ни атрибутов, а имена всех –> <!– братьев уникальны –>

<xsl:for-each-group select="*" group-by="name()"> <xsl:choose>

<xsl:when test="not(*) and count(current-group()) eq 1"> <xsl:attribute name="{local-name(.)}">

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

<!– Копировать атрибуты дочернего элемента в родительский –> <xsl:copy-of select="@*"/> </xsl:when> <xsl:otherwise>

<xsl:apply-templates select="current-group()"/> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:copy> </xsl:when> <xsl:otherwise> <xsl:copy>

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

</xsl:otherwise> </xsl:choose>

</xsl:template>

</xsl:stylesheet>

Обратите внимание, что элемент test теоретически можно было бы преобразо­вать в атрибут E1. Собственно, наша таблица стилей даже попытается это сделать. Но потерпит неудачу, так как в XSLT копирование атрибутов допускается только перед копированием каких-либо других узлов. Если вам когда-нибудь придется иметь дело с такими неудачными документами, как этот, то надо будет выполнить два прохода. На первом проходе не преобразуйте элементы в атрибуты, а просто скопируйте их, пометив специальным атрибутом, показывающим, что они подходят для преобразо­вания. А на втором проходе сначала преобразуйте все помеченные элементы в атри­буты их родителей, а затем скопируйте прочие дочерние элементы без изменения.

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

<E1>

<E2>

<e31>a</e31> <e32>b</e32> <e33>c</e33> </E2>

<test>a</test> <E2>

<e31>u</e31> <e32>v</e32> <e33>w</e33> </E2> <E2>

<e31>x</e31> <e32>y</e32> <e33>z</e33> </E2> </E1>

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

По теме:

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