- Altova
- Saxon
<?xml-stylesheet type="text/xsl" href="trans.xslt"?>
прописана таблица преобразования, совместимая с XSLT 1.0 - и наслаждайтесь результатом! (Тут, правда, следует учесть, что локальный файл не все браузеры корректно откроют - лучше разместить его на каком-то из ваших сайтов, либо поднять сервер локально и загрузить с него - применяет XSLT-стили к локальным файлам разве что FireFox последних версий...)Так вот, в ситуации, когда XSLT 1.0 процессоров - хоть попой ешь, а XSLT 2.0 - в высшей степени скудно, Вашему покорному слуге недавно подумалось - а почему бы не использовать здесь примерно тот же паттерн, что я уже довольно давно и активно применяю по отношению к стеку технологий JavaScript/CSS/HTML, решая проблему кроссбраузерности без потери легковесности решений?
Тем более, что технология к этому располагает. XSLT-документы, фактически, представляют собой XML-документы, а потому к ним, в свою очередь, так же применим любой инструментарий работы с XML. А значит, можно написать XSLT-документ, который будет применяться к другому XSLT-документу, что бы преобразовать его. Зачем же может понадобиться преобразование одного XSLT-документа в другой? Для того, что бы в первом без особых заморочек использовать элементы XSLT 2.0 и XPath 2.0 запросы, а в результате преобразования получать пусть и более громоздкий, но корректный и работающий XSLT 1.0 документ.
Давайте прикинем, что нам для этого нужно?
Во-первых, нужно прописать в исходном XSLT 2.0 документе декларацию, ссылающуюся на документ преобразований уже для него. Т.е. начало нашей XSLT 2.0 таблички будет примерно таким:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="xslt2to1.xslt"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
В принципе, это, конечно, необязательно, но лучше это сделать, что бы не забыть, при помощи какого именно файла это преобразование будет осуществляться. Опять же - Altova XMLSpy последней, 13-й версии это дело игнорирует, не разрешая по кнопке "F10" трансформировать документ, который считает именно XSLT-документом, в отличие от того, как она позволяет поступать по отношению к обычным XML-документам, так что приходится осуществлять это преобразование, перейдя на вкладку преобразующего документа и там указывать исходный документ.Во-вторых, нужно, собственно, создать преобразующий документ. Понятное дело, что этот документ крайне желательно так же иметь в виде XSLT 1.0, иначе полностью уйти от XSLT 2.0 движков не получится. Для начала давайте сделаем таблицу, которая будет полностью копировать исходный документ, а потом будем постепенно вносить в неё template`ы, что бы получить на выходе то, что надо:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Если применить эту таблицу к любому XML-документу - получим на выходе его полную копию! Хорошо, но нам же не нужна полная копия, так? Нам нужно преобразование. Для начала, нам нужно убрать, собственно, инструкцию процессору <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
, говорящую о том, что этот документ перед тем, как использоваться, должен быть преобразован согласно указанной XSLT-таблице стилей - выходной же документ, являющийся результатом преобразования, в свою очередь уже не нуждается в преобразовании и по-этому эту инструкцию нужно убрать.Что бы сделать это, достаточно воспользоваться приёмом создания template`а, который имеет более специфическое XPath-выражение в атрибуте matсh, чем "node()" (поскольку селектор node() является максимально общим, практически любой XPath-запрос к тому же элементу будет более специфичным, так что сделать это не сложно ;) ). Тогда, согласно правилам XSLT, процессор для данного узла, либо атрибута выберет именно новый template, а не старый, именно из-за большей специфичности его XPath-запроса. Каким же должен быть этот template? Пустым, поскольку именно пустой template позволит убрать из конечного документа найденный узел. Т.е. убирается ненужная конструкция добавлением в таблицу преобразования следующего элемента:
<xsl:template match="processing-instruction()[local-name()='xml-stylesheet']"/>
Хорошо. Следующий шаг - изменение версии XSLT в документе, ведь мы преобразуем XSLT 1.0 в 2.0 . Это может быть сделано примерно так:<xsl:template match="/xsl:*[local-name()='stylesheet' or local-name()='transform']/@version[.='2.0']">
<xsl:attribute name="version">1.0</xsl:attribute>
</xsl:template>
Ну а дальше - непаханное поле, вполне достойное отдельного проекта на GitHub`е, который я собираюсь создать и потихоньку разрабатывать (когда найду на это время, конечно). В качестве примера можно рассмотреть реализацию довольно удобного атрибута "separator" в элементе value-of, который появляется в XSLT 2.0 и отсутствует в XSLT 1.0 . Этот атрибут задаёт символы, которые необходимо вставить между результатами выборки, проставленными в XPath-выражении атрибута "select". Например, выражение<xsl:value-of select="'1'|'2'|'3'" separator=", "/>
выведет строчку:"1, 2, 3"Для того, что бы добиться подобного поведения в XSLT 1.0, нужно написать примерно такой вот громоздкий код:
<xsl:for-each select="'1'|'2'|'3'">
<xsl:value-of select="."/>
<xsl:if test="not(position()=last())">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
Соответственно, преобразование может выглядеть примерно так:<xsl:template match="xsl:value-of[@separator]">
<xsl:element name="xsl:for-each">
<xsl:attribute name="select">
<xsl:value-of select="@select"/>
</xsl:attribute>
<xsl:element name="xsl:value-of">
<xsl:attribute name="select">.</xsl:attribute>
</xsl:element>
<xsl:element name="xsl:if">
<xsl:attribute name="test">not(position()=last())</xsl:attribute>
<xsl:element name="xsl:text">
<xsl:value-of select="@separator"/>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:template>
Вот примерно как-то так...P.S. Остался наиболее хитрый вопрос - о том, что делать с спецификой именно XPath 2.0 - запросов. Дело в том, что напрашивающиеся тут RegExp`ы в XSLT 1.0 (читай - XPath 1.0) не поддерживаются, что создаёт, мягко говоря, некоторые трудности. Что тут можно сделать? Либо отказаться от идеи использования в качестве процессора преобразования XSLT 1.0 процессор и по крайней мере для преобразования таблиц юзать процессор XSLT 2.0, либо использовать механизм расширений. Например, для Xalan`а пример работы можно подсмотреть тут.
Комментариев нет:
Отправить комментарий