Переопределение шаблона XSLT - PullRequest
1 голос
/ 18 мая 2009

У меня небольшой вопрос относительно переопределения шаблона XSLT. Для этого сегмента моего XML:

<record>
  <medication>
    <medicine>
      <name>penicillin G</name>
      <strength>500 mg</strength>
    </medicine>
  </medication>
</record>

В моем листе XSLT у меня есть два шаблона в следующем порядке:

 <xsl:template match="medication">
   <xsl:copy-of select="." />
   </xsl:template>
   <xsl:template match="medicine/name">
   <text>!unauthorized information!</text>
 </xsl:template>

Что я хочу сделать, так это скопировать все содержимое элемента лекарства на выход, кроме элемента «имя» (или любого другого элемента, который я явно определил) Окончательный XML будет показан пользователю в формате RAW XML. Другими словами, я хочу получить результат:

<record>
  <medication>
    <medicine>
      <text>! unauthorized information!</text>
      <strength>500 mg</strength>
    </medicine>
  </medication>
</record>

В то время как я получаю тот же XML, что и входные данные, то есть без замены элемента на текст. Любые идеи, почему второй шаблон соответствия не перекрывает элемент имени в первом? Заранее спасибо

- Али

Ответы [ 2 ]

5 голосов
/ 18 мая 2009

Шаблон заказа не имеет значения. Единственный случай, когда он может быть рассмотрен (и это зависит от процессора), - это когда у вас есть неразрешимый конфликт, то есть состояние ошибки. В этом случае для XSLT-процессора разрешается восстанавливаться после ошибки, выбирая последнюю. Однако вы никогда не должны писать код, который зависит от этого поведения.

В вашем случае приоритет шаблона даже не проблема. У вас есть два разных правила шаблона, одно из которых соответствует <medication> элементам, а другое соответствует <name> элементам. Они никогда не столкнутся, поэтому речь не идет о приоритете шаблона или переопределении. Проблема в том, что ваш код никогда не применяет шаблоны к элементу <name>. Когда вы говорите <xsl:copy-of select="."/> на <medication>, вы говорите: «выполнить глубокую копию <medication>». Единственный способ, которым любое из правил шаблона будет срабатывать для узлов-потомков, - это если вы явно применяете шаблоны (используя <xsl:apply-templates/>.

Решение, которое я имею для вас, в основном такое же, как у alamar, за исключением того, что оно использует отдельный «режим» обработки, который изолирует правила от всех других правил в вашей таблице стилей. Общий шаблон match="@* | node()" вызывает рекурсивное применение правил шаблона к дочерним элементам (и атрибутам), что дает вам возможность переопределить поведение для определенных узлов.

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <!-- ...placeholder for the rest of your code... -->
  <xsl:template match="/record">
    <record>
      <xsl:apply-templates/>
    </record>
  </xsl:template>
  <!-- end of placeholder -->

  <xsl:template match="medication">
    <!-- Instead of copy-of, whose behavior is to always perform
         a deep copy and cannot be customized, define your own
         processing mode. Rules with this mode name are isolated
         from the rest of your code. -->
    <xsl:apply-templates mode="copy-medication" select="."/>
  </xsl:template>

          <!-- By default, copy all nodes and their descendants -->
          <xsl:template mode="copy-medication" match="@* | node()">
            <xsl:copy>
              <xsl:apply-templates mode="copy-medication" select="@* | node()"/>
            </xsl:copy>
          </xsl:template>

          <!-- But replace <name> -->
          <xsl:template mode="copy-medication" match="medicine/name">
            <text>!unauthorized information!</text>
          </xsl:template>

</xsl:stylesheet>

Правило для "medicine/name" переопределяет правило для "@* | node()", потому что формат шаблона (который содержит "/") делает его приоритет по умолчанию (0,5) выше, чем приоритет по умолчанию "node()" (-1,0).

Полное, но краткое описание того, как работает приоритет шаблона, можно найти в «Как работает XSLT» на моем веб-сайте.

Наконец, я заметил, что вы упомянули, что хотите отобразить «RAW XML» для пользователя. Означает ли это, что вы хотите отобразить, например, XML со всеми начальными и конечными тегами в браузере? В этом случае вам нужно будет избежать разметки (например, "&lt;" для "<"). Проверьте утилиту XML-to-string на моем веб-сайте. Дайте мне знать, если вам нужен пример того, как его использовать.

2 голосов
/ 18 мая 2009

Добавить

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

к вашему <xsl:template match="medicine/name">

И удалить <xsl:template match="medication"> в целом!

<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">

 <xsl:template match="medicine/name">
   <text>!unauthorized information!</text>
 </xsl:template>

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

</xsl:stylesheet>
...