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>
Другие вопросы по тегам