Александр Самиляк
21 июня 2011 |
|
Задача. | Разобрать по косточкам теорию и практику использования временных деревьев в XSLT. |
||
XSL-трансформация имеет
Вспомним синтаксис элементов <xsl:variable>, <xsl:param> и <xsl:with-param>. У них можно указать атрибут select, при этом тело элемента должно быть пустым. Тогда тип переменной будет строкой (select="'Chicago, Illinois'"), числом (select="331"), булевым типом (select="true()") или набором узлов
Однако можно не указывать атрибут select, а присвоить значение в теле элемента. Вот тогда в переменной (или параметре) будет временное дерево. Его можно создать статическим кодом:
<xsl:variable name="ER_characters"> <item>Mark Greene</item> <item sexy="true">Doug Ross</item> <item>Susan Lewis</item> <item>John Carter</item> </xsl:variable>
А можно и вызовом другого шаблона, который возвращает какой-либо заранее неизвестный XML:
<xsl:variable name="ER_characters"> <xsl:call-template name="get_ER_characters" /> </xsl:variable>
Называть такое хозяйство временным деревом первым стал Майкл Кэй (небритый мужик на обложке красной книжки XSLT). Всем понравилось, и термин temporary tree решили даже включить в спецификацию XSLT 2.0.
С этим фрагментом результирующего дерева нельзя сделать ничего, кроме примитивных строковых операций. То есть можно сделать, например,
<xsl:template name="rounded_corners"> <xsl:param name="content" /> <xsl:if test="string($content)"> <div class="rounded"> <b class="c lt" /><b class="c rt" /> <xsl:copy-of select="$content" /> <b class="c rb" /><b class="c lb" /> </div> </xsl:if> </xsl:template> <xsl:call-template name="rounded_corners"> <xsl:with-param name="content"> <!-- Это временное дерево --> <p> <b>ER</b> is an American medical drama television series aired on NBC. </p> </xsl:with-param> </xsl:call-template>
Однако на этом радость кончается, ведь в XSLT 1.0 мы не можем обойти временное дерево (тьфу, то есть RTF)
<xsl:variable name="ER_cast"> <item>Anthony Edwards</item> <item sexy="false">George Clooney</item> <item>Sherry Stringfield</item> <item>Noah Wyle</item> </xsl:variable> <xsl:value-of select="$ER_cast/item[3]" />
не получится. Путь к светлому будущему нам преградит ошибка (на примере трансформатора libxslt):
Invalid type runtime error: file ... line ... element value-of XPath evaluation returned no result
А все потому, что в спецификации 1.0 черненькими буковками по беленькому фончику написано: «Операции / или
Можно было уже начинать паковать чемоданы, как выяснилось, что не все готовы мириться с таким произволом властей. Некоторые ребята из XSL-сообщества собрались и подумали: «А почему бы нам не сделать такую функцию, которая будет преобразовывать тип RTF в тип
Это ли не идея судьбы? Ведь данные типа
Весь проект EXSLT разделен на модули, которые содержат наборы функций определенного предназначения — common, dates and times, math... Функция
Итак, что нужно делать, чтобы праздник пришел в каждый XSL-шаблон:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" > <xsl:variable name="ER_cast"> <item>Anthony Edwards</item> <item sexy="false">George Clooney</item> <item>Sherry Stringfield</item> <item>Noah Wyle</item> </xsl:variable> <xsl:template match="/"> <xsl:value-of select="exsl:node-set($ER_cast)/item[3]" /> </xsl:template> </xsl:stylesheet>
Хозяйке на заметку |
Этот и все последующие примеры предполагают, что в XSL-файле не объявлен неймспейс по умолчанию (например, xmlns="http://www.w3.org/1999/xhtml"). Об этой и еще одной тонкости работы с временными деревьями можно почитать в отдельной части. |
Мы объявили неймспейс с префиксом exsl и URI, равным http://exslt.org/common. Этот URI зашит в трансформатор, и, видя такой URI, он сразу понимает, что надо подключить модуль common библиотеки расширений EXSLT. Префикс может быть любым, это внутреннее имя шаблона, но мы используем exsl.
Надо сказать, что это стандартный способ подключения к XSL-шаблону сторонних модулей — уникальный URI этого стороннего модуля присваивается выбранному префиксу, а дальше в нужном месте вызывается функция этого модуля конструкцией вида:
префикс : имя функции (аргументы)
Назад к примеру. Мы создаем временное дерево и сохраняем его в переменную $ER_cast, которая получает тип RTF. Затем преобразуем эту переменную к типу
И вот это, товарищи, уже работает. На выходе получаем заветную строчку
Внимательный читатель в этом примере заметит важное: на первом уровне временного дерева может находиться более одного элемента (у нас 4 элемента <item>), тогда как на первом уровне дерева входящего всегда должен быть один и только один корневой элемент, чего требует
Также замечу, что упомянутое выражение exsl:node-set($ER_cast) возвращает именно корень временного дерева,
В следующей части мы займемся применением временных деревьев.
© 19952025 Студия Артемия Лебедева
|