Главная » XSLT » Включение условий в выражения if

0

Задача

У вас есть сложная XSLT-программа, которая оказалась излишне длинной из- за того, что запись условий if-then-else в XML слишком многословна.

Решение

XPath 1.0

В XPath 1.0 есть несколько приемов, позволяющих обойтись без громоздкого выражения xsl:choose в простых ситуациях. Они основаны на том, что в число­вом контексте значение false преобразуется в 0, а значение true – в 1.

Так, например, минимум, максимум и абсолютную величину можно вычис­лить непосредственно. Ниже предполагается, что $x и $y – целые числа.

(: min :)

($x <= $y) * $x + ($y < $x) * $y

(: max :)

($x >= $y) * $x + ($y > $x) * $y

(: abs :)

(1 – 2 * ($x < 0)) * $x

XPath 2.0

Для описанных в предыдущем разделе операций (min, max, abs) в XPath 2.0 появились специальные функции. Чтобы реализовать другие простые условия, можно воспользоваться новой конструкцией – выражением if.

(: Вместо отсутствующего атрибута взять значение по умолчанию 10. :) if (@x) then @x else 10

(: Вместо отсутствующего элемента взять значение по умолчанию ‘unauthorized’. :)

if (password ) then password else ‘unauthorized’

(: Защита от деления на нуль. :) if ($d ne 0) then $x div $d else 0

(: Текст элемента para, если он содержит хотя бы один непробельный символ, иначе единственный пробел. :)

if (normalize-space(para)) then string(para) else ‘ ‘

Обсуждение

Если вы давно работаете с XSLT 1.0, то, наверное, кривитесь всякий раз, как нужно включить в шаблон условно выполняемый код, По себе знаю, часто прихо­дилось применять встроенные в XSLT средства сопоставления с образцом, лишь бы обойтись без условий. И дело не в том, что такой код сложнее или менее эффек­тивен, просто он получается очень многословным. Простое условие xsl:if еще терпимо, но, если дело доходит до ветвления if-then-else, то приходится обращать­ся к громоздкой конструкции xsl:choose. Печально это.

В XSLT 2.0 появилась альтернатива, но она является частью XPath 2.0, а не самого языка XSLT. При первом знакомстве складывается впечатление, что XPath только испортили введением конструкции, которая применяется для управления логикой выполнения программы. Однако, начав активно ис­пользовать XPath 2.0, вы скоро поймете, что и XPath, и XSLT только выиграли от такого решения. К тому же условные выражения XPath 2.0 не отменяют кон­струкции xsl:if, а лишь уменьшают потребность в ней в тех ситуациях, когда она выглядит особенно неуклюже. В качестве иллюстрации рассмотрим та­кой фрагмент:

<!— XSLT 1.0 —> <xsl:variable name="size">

<xsl:choose>

<xsl:when test="$x &gt; 3">большой</xsl:when> <xsl:otherwise>мaленький</xsl:otherwise> </xsl:choose> </xsl:variable>

<!— XSLT 2.0 —>

<xsl:variable name="size" select="if ($x gt 3) then ‘большой’ else ‘маленький’ "/>

Полагаю, что большинство читателей предпочтет второе решение, возможное в XSLT 2.0.

Следует сделать важное замечание об условных выражениях в XPath: ветвь else обязательна. Программисты на языке C могут провести аналогию с тернар­ным выражением a ? b : c. Часто в случае, когда разумной альтернативы нет, используют пустую последовательность ().

Условные выражения удобны, когда нужно подставить значения по умолча­нию, а схемы, в которой они были бы определены, не существует.

(: Значение по умолчанию для необязательного атрибута :) if (@optional) then @optional else ‘some-default’ (: Значение по умолчанию для необязательного элемента :) if (optional) then optional else ‘some-default’

Также они оказываются полезны, чтобы избежать неопределенных или неже­лательных результатов при вычислении выражений. В следующем примере у нас, вероятно, есть причина предпочесть в качестве результата нуль, а не бесконеч­ность number(‘Infinity’):

if ($divisor ne 0) then $dividend div $divisor else 0

Условия могут и более сложными. В следующем коде производится перекоди­ровка списка из нескольких значений:

if (size eq ‘XXL’) then 50 else if (size eq ‘XL’) then 45 else if (size eq ‘L’) then 40 else if (size eq ‘M’) then 34 else if (size eq ‘S’) then 32 else if (size eq ‘XS’) then 29 else -1

Однако в данном случае предпочтительнее решение, основанное на последо­вательностях, особенно если заменить литеральные строки переменными, кото­рые читаются из внешнего XML-файла:

(50,45,40,34,32,29,-1)[(index-of((‘XXL’,’XL’,’L’,’M’,S’,’XS’)), size),7)[1]]

Здесь предполагается, что у контекстного узла есть единственный дочерний элемент с именем size, в противном случае выражение окажется некорректным (хотя это можно исправить, написав size[1] ). Мы также полагаемся на то, что функция index-of возвращает пустую последовательность, если элемент не найден. Именно для этого мы добавили заведомо отсутствующий элемент 7, реа­лизовав тем самым поведение ветви else.

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии