Александр Самиляк
12 июля 2011 |
|
Задача. | Разобрать по косточкам теорию и практику использования временных деревьев в XSLT. |
||
Весь написанный нами XSL-код исполняет XSL-трансформатор (он же XSL-процессор). Для нас важной характеристикой трансформатора является его поддержка XSLT версии 2.0. Важно это потому, что в версии 2.0 магический тип данных Result Tree Fragment был истреблен как класс (см. второй пункт) и ему на смену пришли полноценные временные деревья. То есть для XSLT 2.0 временные деревья являются родным понятием, и нам не нужно делать никаких преобразований типа exsl:node-set(RTF). Поэтому, написав:
<xsl:variable name="ER_episodes"> <item>24 Hours</item> <item>Day One</item> <item>Going Home</item> </xsl:variable>
мы можем немедленно пользоваться этим деревом и заходить внутрь него, как если бы это был обычный набор узлов:
<xsl:value-of select="$ER_episodes/item[2]" />
Забегая немного вперед, скажу, что производители трансформаторов очень неохотно реализуют поддержку XSLT 2.0, поэтому не стоит привыкать к такой роскоши. Это вдвойне обидно потому, что, кроме нативных временных деревьев, в XSLT 2.0 есть куча других полезных функций, которых нам порой так не хватает. Но есть и хорошая новость — в большинстве процессоров присутствует та или иная поддержка EXSLT. При этом модуль common (а значит, и наша заветная функция node-set()) обычно входит в эту поддержку. Вообще, EXSLT содержит много разных модулей, среди которых и расширенная работа со строками, и регулярные выражения, и пользовательские XPath-функции, и генерация случайного числа. Все зависит лишь от поддержки каждого модуля трансформаторами.
А трансформаторов этих в природе существует не меньше десятка. Однако если говорить о серверной части сайта, то на момент написания данной статьи более или менее распространены четыре — libxslt, Xalan, Saxon и MSXML. Пару слов о каждом из них.
libxslt — самый широко используемый и, наверно, самый старинный процессор, который базируется на другой бородатой библиотеке libxml. Написаны они на C, что позволяет им «выстреливать», как АК-47, на любых трансформациях. Сейчас он понимает только XSLT 1.0, но похоже, что работа над поддержкой XSLT 2.0 ведется.
В libxslt реализованы почти все модули EXSLT, в том числе функция node-set(), поэтому на этом процессоре временные деревья можно использовать без каких-либо опасений.
Xalan — трансформатор Apache, имеющий версии на Java и C++. Тем не менее, ассоциируется он в первую очередь с Java, потому что входит в стандартную поставку JDK. Поработав с этим процессором довольно продолжительное время на нескольких проектах, могу сказать, что с ним иногда случаются необъяснимые глюки. Оно, в общем, и неудивительно — последняя версия выпускалась в 2007 году. Короче, этот трансформатор потихоньку устаревает, и его место в Java-сообществе занимает Saxon.
В Xalan тоже реализована функция node-set() из EXSLT.
Saxon — самый перспективный трансформатор, написанный не кем иным, как небритым мужиком Майклом Кэем. Есть две реализации — на Java и на .NET. Этот трансформатор выделяется на фоне других полной поддержкой XSLT 2.0.
В Saxon временные деревья являются полноценным типом данных (XSLT 2.0 ведь), и никаких преобразований функцией node-set() делать не нужно, все работает из коробки. Однако поддержка EXSLT в Saxon тоже есть, а это значит, что код можно сделать переносимым. Поэтому моя рекомендация: даже если на проекте Saxon, все равно следует вызывать exsl:node-set(). Это позволит при необходимости использовать этот же XSL-код на других трансформаторах.
MSXML — набор
Насчет EXSLT: он не поддерживается. Вместо этого предлагается пользоваться расширением самого Microsoft, в котором есть аналогичная функция node-set(). В общем, это лучше, чем ничего, но использовать тот же код на других трансформаторах, понятно, не получится. Замечу, что и libxslt, и Xalan, и Saxon тоже имеют свои личные проприетарные расширения, но при этом они не отключают поддержку EXSLT, проявляя заботу о переносимости кода. А Редмонд в своем репертуаре. Мочить.
Однако 10 лет верстки под IE6 прибавили человечеству изобретательности в борьбе с пакостями Microsoft, поэтому предлагаю хак, найденный в блоге одного хорошего человека. Следим за руками:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:msxml="urn:schemas-microsoft-com:xslt" extension-element-prefixes="exsl msxml" > <!-- Что-то похожее на CSS-expression --> <msxml:script language="JScript" implements-prefix="exsl"> this['node-set'] = function(x) { return x; } </msxml:script> <xsl:template match="/"> <xsl:variable name="ER_episodes"> <item>24 Hours</item> <item>Day One</item> <item>Going Home</item> </xsl:variable> <xsl:value-of select="exsl:node-set($ER_episodes)/item[2]" /> </xsl:template> </xsl:stylesheet>
На всех четырех трансформаторах получим:
Day One
Этот <msxml:script>, работая в MSXML, перенаправляет вызов exsl:node-set(RTF) на msxml:node-set(RTF), при этом не мешая работать другим трансформаторам, которые просто игнорируют незнакомую конструкцию. Кросстрансформаторность такая получается.
Данный прием сильно помогает, когда требуется повышенная переносимость XSL-кода (например, на клиентской стороне в браузере), и работает, даже если <msxml:script> определить в импортируемом XSL-файле.
main.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" > <xsl:import href="import.xsl" /> <xsl:template match="/"> <xsl:variable name="ER_episodes"> <item>24 Hours</item> <item>Day One</item> <item>Going Home</item> </xsl:variable> <xsl:value-of select="exsl:node-set($ER_episodes)/item[2]" /> </xsl:template> </xsl:stylesheet>
import.xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:msxml="urn:schemas-microsoft-com:xslt" extension-element-prefixes="exsl msxml" > <msxml:script language="JScript" implements-prefix="exsl"> this['node-set'] = function(x) { return x; } </msxml:script> </xsl:stylesheet>
Подытоживая, приведу сводную таблицу, отражающую все вышесказанное.
libxslt | Xalan | Saxon | MSXML | |
---|---|---|---|---|
Версия XSLT | 1.0 | 1.0 | 2.0 | 1.0 |
Поддержка exsl:node-set() | + | + | + | − |
URI личного расширения | http:// xmlsoft.org/ XSLT/ namespace | http:// xml.apache.org/ xalan | http:// saxon.sf.net/ | urn:schemas-microsoft-com:xslt |
Функция node-set() из личного расширения | node-set(RTF) | nodeset(RTF) | − | node-set(RTF) |
Следующая часть будет посвящена производительности трансформаторов при работе с временными деревьями.
© 19952024 Студия Артемия Лебедева
|