XSLT-решение, необходимое для: атрибутного узла не может быть создан после дочерних элементов содержащего элемента
Предыстория состоит в том, что это старое приложение ColdFusion, которое обновляется до ColdFusion 11. Я уже определил предметную ошибку, которую я сейчас вижу, потому что процессорам XSLT 1.0 было разрешено игнорировать эту ошибку и "восстанавливать", игнорируя xsl: инструкция атрибута. (Спасибо, реф. Майкл Кей.)
Я не могу полностью обдумать решение, поэтому вот несколько упрощенных кодов:
XML
<?xml version="1.0" encoding="UTF-8"?>
<Organization>
<Subscriber>
<data>
<struct>
<var name='DEPENDENT'>
<array length='1'>
<struct>
<var name='PROVIDER'>
<struct>
<var name='PROVIDERCODE'>
<string>A4321</string>
</var>
<var name='TYPE'>
<string>Primary Care Provider</string>
</var>
</struct>
</var>
</struct>
</array>
</var>
</struct>
</data>
</Subscriber>
</Organization>
Вы можете распознать это как старый формат WDDX. XSLT ниже преобразует этот уродливый, глубоко вложенный формат в более чистый / простой формат XML.
XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="var">
<xsl:choose>
<xsl:when test="translate(@name, 'TEX', 'tex') = 'text'">
<xsl:value-of select="./string"/>
</xsl:when>
<xsl:when test="translate(@name, 'TYPE', 'type') = 'type'">
<xsl:attribute name="type">
<xsl:value-of select="./string"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="child::array">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')}">
<xsl:apply-templates/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="struct">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="array">
<xsl:param name="parentname" select="translate(../@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"/>
<xsl:for-each select="struct">
<xsl:element name="{$parentname}">
<xsl:if test="./var[translate(@name, 'RELATIONSHPCD', 'relationshpcd') = 'relationshipcode']">
<xsl:attribute name="relationshipcode"><xsl:value-of select="./var[translate(@name, 'RELATIONSHPCD', 'relationshpcd') = 'relationshipcode']"/></xsl:attribute>
</xsl:if>
<xsl:apply-templates/>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template match="string|number">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="boolean">
<xsl:value-of select="@value"/>
</xsl:template>
<xsl:template match="Subscriber">
<subscriber>
<xsl:apply-templates/>
</subscriber>
</xsl:template>
<xsl:template match="Organization">
<organization>
<xsl:apply-templates/>
</organization>
</xsl:template>
</xsl:stylesheet>
К сожалению, случай с атрибутами атрибутов WDDX не согласуется, поэтому здесь есть команды преобразования, которые, к сожалению, делают код немного более сложным.
Ожидаемый результат
<?xml version="1.0" encoding="UTF-8"?>
<organization>
<subscriber>
<dependent>
<provider type="Primary Care Provider">
<providercode>A4321</providercode>
</provider>
</dependent>
</subscriber>
</organization>
Эта проблема
Проблема возникает из-за того факта, что в приведенном выше примере "код поставщика" может быть добавлен к узлу "поставщика" до того, как будет обнаружен узел "типа", в результате чего "узел атрибута не может быть создан после дочерних элементов содержащего элемент"error.o
Обратите внимание, что я упростил приведенный выше пример для ясности. Существует примерно полдюжины узлов "var" с определенными атрибутами "name", которые должны быть вставлены как атрибуты, в то время как другие простые добавляются непосредственно как узлы.
Любая помощь в решении этой проблемы будет принята с благодарностью.
Заранее спасибо.
Павел
2 ответа
К сожалению, случай с атрибутами атрибутов WDDX не согласуется, поэтому здесь есть команды преобразования, которые, к сожалению, делают код немного более сложным.
Жаль, что вы не взяли это (и другие несущественные) из своего примера - см.: https://stackru.com/help/mcve
IIUC, что-то вроде следующего решит вашу проблему. Идея состоит в том, чтобы применять шаблоны, создающие атрибуты, перед шаблонами, которые создают дочерние элементы.
...
<xsl:template match="var[not(@name='TYPE')]">
<xsl:choose>
<xsl:when test="translate(@name, 'TEX', 'tex') = 'text'">
<xsl:value-of select="./string"/>
</xsl:when>
<xsl:when test="child::array">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{translate(@name,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')}">
<xsl:apply-templates select="struct/var[@name='TYPE']"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="var[@name='TYPE']">
<xsl:attribute name="type">
<xsl:value-of select="./string"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="struct">
<xsl:apply-templates select="var[not(@name='TYPE')]"/>
</xsl:template>
...
Приведенная ниже таблица стилей основана на приведенном вами минимальном примере и некоторых упомянутых вами требованиях (несовместимые случаи имен атрибутов). Я не уверен, что этого достаточно для работы с документами реального мира, но это может как-то помочь вам и, возможно, дать вам некоторые идеи.
Я использовал три шаблона, чтобы справиться с var
элемент, основанный на типе потомка, который он содержит (array
, struct
или же string
), а некоторые предикаты проверяют некоторые значения атрибутов (например, providercode
). Если применить минимальный пример, который вы предоставили, он даст ожидаемый результат. Возможно, вам придется внести некоторые коррективы, чтобы адаптировать его к вашей реальной проблеме.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="lower">abcdefghijklmnopqrstuvwxyz</xsl:variable>
<xsl:variable name="upper">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable>
<xsl:template match="/">
<organization>
<subscriber>
<xsl:apply-templates select="Organization/Subscriber/data/struct/var"/>
</subscriber>
</organization>
</xsl:template>
<xsl:template match="var[array]">
<xsl:element name="{translate(@name,$upper,$lower)}">
<xsl:apply-templates select="array/struct/var"/>
</xsl:element>
</xsl:template>
<xsl:template match="var[struct]">
<xsl:element name="{translate(@name,$upper,$lower)}">
<xsl:attribute name="type">
<xsl:value-of select="struct/var[translate(@name,$upper,$lower)='type']/string"/>
</xsl:attribute>
<xsl:apply-templates select="struct/var"/>
</xsl:element>
</xsl:template>
<xsl:template match="var[string][translate(@name,$upper,$lower) = 'providercode']">
<xsl:element name="{translate(@name,$upper,$lower)}">
<xsl:value-of select="string"/>
</xsl:element>
</xsl:template>
<xsl:template match="string"/>
</xsl:stylesheet>
Я объявил lower
а также upper
в качестве переменных, поэтому было бы проще использовать translate()
функция для преобразования строк в нижний регистр.
Вы можете опробовать этот код в этой скрипте XSLT, поэкспериментировать и посмотреть результаты преобразования.