В настоящее время принятое решение создает требуемые атрибуты в фиксированном жестко заданном порядке, который не соответствует порядку атрибутов элемента format
, содержащегося в исходном XML-документе .
Такое решение может быть приемлемым в случае HTML, но могут быть и другие случаи, когда желательно сохранение порядка атрибутов.
Этот ответ представляет решение, которое сохраняет порядок атрибутов . Кроме того, имена атрибутов не жестко запрограммированы в коде (может содержаться в отдельном документе), что делает код абсолютно независимым от любых изменений имен исходных атрибутов или соответствующих имен элементы, которые будут сгенерированы.
Вот простое решение XSLT 1.0, которое занимает одну таблицу стилей xslt и не использует <xsl:apply-imports>
.
Это решение не зависит от порядка атрибутов или их количества и не знает их, и работает правильно, если этот порядок или количество атрибутов каким-либо образом изменяется:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:mapping>
<map old="strikeout">strike</map>
</my:mapping>
<xsl:variable name="vMap" select="document('')/*/my:mapping/*"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="format">
<xsl:variable name="vAttribs" select="@*"/>
<xsl:call-template name="genAttribs">
<xsl:with-param name="pAttribs" select="$vAttribs"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="genAttribs">
<xsl:param name="pAttribs" select="/.."/>
<xsl:choose>
<xsl:when test="$pAttribs">
<xsl:variable name="vMappedElemName"
select="$vMap[@old = name($pAttribs)]"/>
<xsl:variable name="vElemName" select=
"concat($vMappedElemName,
substring(name($pAttribs[not($vMappedElemName)])
,1,1)
)
"/>
<xsl:element name="{$vElemName}">
<xsl:call-template name="genAttribs">
<xsl:with-param name="pAttribs"
select="$pAttribs[position() > 1]"/>
</xsl:call-template>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
когда это преобразование применяется к следующему документу XML :
<root>
Pacman
<format bold="1" italic="1"
underscore="1"
strikeout="1">rules</format>!
</root>
желаемый, правильный результат выдается :
<root>
Pacman
<b>
<i>
<u>
<strike>rules</strike>
</u>
</i>
</b>!
</root>
Объяснение
Атрибуты обрабатываются один за другим для создания вложенных элементов. Только после обработки последнего атрибута мы генерируем текстовый узел родителя атрибута в теле сгенерированного внутреннего элемента
Мы поддерживаем таблицу сопоставления - отображение attribute-name --> element-name
необходимо указывать только в том случае, если требуемый перевод имени атрибута в имя элемента не является просто первой буквой имени атрибута. Если в таблице сопоставления указано имя атрибута, то мы используем строковое значение этого элемента таблицы сопоставления для генерации имени элемента.
Если имя атрибута не указано в таблице сопоставления, то для имени элемента мы используем первую букву имени атрибута.
Таким образом, решение не требует каких-либо изменений, если указан какой-либо новый атрибут, а имя его соответствующего (предназначенного для создания) элемента является первой буквой имени этого атрибута .
Наконец : обратите внимание, что таблица сопоставления не обязательно должна быть частью кода XSLT (она здесь только для удобства) - в сценарии реального мира это будет отдельный документ XML ( файл) и код XSLT никогда не придется обновлять , когда необходимо добавить новое отображение attribute-name --> element-name
.