XSL для преобразования XML-фильтрации только значения элементов и атрибутов?
Мне нужно преобразовать следующий XML в XML, который имеет те же элементы и атрибуты, за исключением значений, которые могут быть локализованы - в основном английские фразы.
Некоторые из элементов (<footnote>
) и атрибуты являются необязательными (<display_data_type>
), и я хотел бы иметь возможность сделать это в общем - без шаблона для каждого элемента. Это возможно?
Конечная цель состоит в том, чтобы иметь возможность сравнить версию XML по умолчанию с локализованной версией, игнорируя локализованные строки.
Например следующее:
<data_schema>
<field symbol="ACCOUNT" type="string" name="Account Number">
<validators>
<maxlength>6</maxlength>
</validators>
<description>The account number</description>
<example>123456</example>
<default_value></default_value>
</field>
<field symbol="POSTAL_CODE" type="string" name="Postal Code">
<description>Postal Code for account</description>
<example>22022</example>
<footnote>Does not apply to certain accounts</footnote>
<default_value></default_value>
</field>
<field symbol="DISCOUNT" type="string" name="Discount Percentage" display_data_type="percentage">
<description>Descount determined by account</description>
<example>1.5%</example>
<default_value></default_value>
</field>
</data_schema>
будет преобразован в:
<data_schema>
<field symbol="ACCOUNT" type="string" name="">
<validators>
<maxlength>6</maxlength>
</validators>
<description/>
<example/>
<default_value/>
</field>
<field symbol="POSTAL_CODE" type="string" name="">
<description/>
<example/>
<footnote/>
<default_value/>
</field>
<field symbol="DISCOUNT" type="string" name="" display_data_type="percentage">
<description/>
<example/>
<default_value/>
</field>
</data_schema>
2 ответа
Вот пример. Подобный шаблон при применении должен создавать копию дерева, за исключением текстовых областей и текста атрибутов для атрибутов, которые не являются символом или типом.
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:for-each select="@*">
<xsl:choose>
<xsl:when test="name() = 'symbol' or name() = 'type'">
<xsl:copy-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="{name()}"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<xsl:apply-templates select="*"/>
</xsl:element>
</xsl:template>
Вот другой подход. Это основано на шаблоне проектирования XSLT "Identity Transform", который в своей основе просто копирует все узлы как есть.
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
Вы бы расширили это, чтобы добавить другие соответствия шаблонов для соответствия узлам, над которыми вы хотите выполнить определенные действия, которые в вашем случае должны удалить текст. Необходимые шаблоны будут зависеть от точных правил преобразования.
Если вы хотите удалить текст из определенных элементов и атрибутов, вы должны добавить следующие два шаблона, чтобы скопировать узлы, но без текста:
<xsl:template match="description|example|footnote|default_value">
<xsl:copy/>
</xsl:template>
<xsl:template match="@name">
<xsl:attribute name="{name()}"/>
</xsl:template>
Таким образом, в этом случае текст описания элементов, например, сноска и default_value удаляются вместе с атрибутом @name. Все остальные узлы будут скопированы как есть, с их текстом.
С другой стороны, если у вас есть определенный список элементов и атрибутов, которые вы хотите изменить без изменений, вы можете добавить шаблоны так:
<xsl:template match="field/*[not(self::validators)]">
<xsl:copy/>
</xsl:template>
<xsl:template match="@symbol|@type|@display_data_type">
<xsl:copy/>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}"/>
</xsl:template>
Итак, для элемента validators, на самом деле вы говорите, удалите все, что не является элементом validators. Элементы validators будут скопированы шаблоном преобразования идентичности. Для атрибутов я показал немного другой подход, в котором атрибуты, которые нужно сохранить, были явно перечислены, и у вас есть второй шаблон для удаления текста из всех остальных.
Вот два полных XSLT в этом случае.
Первый для удаления текста из определенных узлов
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="description|example|footnote|default_value">
<xsl:copy/>
</xsl:template>
<xsl:template match="@name">
<xsl:attribute name="{name()}"/>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Второй для хранения текста в определенных узлах
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="field/*[not(self::validators)]">
<xsl:copy/>
</xsl:template>
<xsl:template match="@symbol|@type|@display_data_type">
<xsl:copy/>
</xsl:template>
<xsl:template match="@*">
<xsl:attribute name="{name()}"/>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
В случае вашего образца документа оба должны выдавать один и тот же результат:
<data_schema>
<field symbol="ACCOUNT" type="string" name="">
<validators>
<maxlength>6</maxlength>
</validators>
<description/>
<example/>
<default_value/>
</field>
<field symbol="POSTAL_CODE" type="string" name="">
<description/>
<example/>
<footnote/>
<default_value/>
</field>
<field symbol="DISCOUNT" type="string" name="" display_data_type="percentage">
<description/>
<example/>
<default_value/>
</field>
</data_schema>