войти зарегистрироваться

XSLT whois

индекс
0,00

Практический XSLT. Использование в качестве шаблонизатора

В сети доступно масса документации по языку XSL. Данный раздел не претендует на роль документации по языку, а лишь кратко, по шагам объясняет, как создать свой XSLT-шаблон.

Описанная ниже схема успешно мною используется уже более 3 лет. По началу я к XSLT относился с большой опаской (особенно, когда разбирал чужие исходники), однако однажды поняв, что к чему, уже не представляю, как без него можно работать.

Рабочий стол

Определим, что нам нужно для работы:
  • Входной XML-документ
  • XHTML-макет шаблона
  • Парсер XML для склейки XML с XSL

У меня входной XML документ выдает CMS-система, в которой каждая страница с материалом собирается в XML-дерево.

К XHTML-макету никаких ограничений нет. Есть лишь определенные рекомендации по верстке, которые позволят значительно сэкономить время на формирование шаблона.

В качестве парсера (сборщика) конечного документа можно использовать браузер. Нужно лишь указать в XML-документы путь к файлу шаблону:
<?xml-stylesheet type="text/xsl" href="template.xsl" ?>

Хотя, как показала практика, этот механизм довольно глючный (мне пришлось пользовать IE). Лучше воспользоваться средствами XML-парсинга языка, на котором написана CMS-система. Я использую Parser (на нем, вообщем-то, у меня вся система и работает).

Входной XML-документ

Для начала разберемся со входным XML-документом. Для того, чтобы использовать XSL нужно иметь полное представление о его структуре.

Я использую следующую схему:
<?xml version="1.0" encoding="windows-1251"?>
Начало

<lang_table>
/>
</lang_table>
<item id="0" parent_id="0" is_published="1" section="1">
Начало
/

<item id="1" parent_id="0" is_published="1" section="1">
Новости
news







Обозначенный выше пример схемы не претендует на свою оптимальность. В силу тех или иных причин, мне он удобен. Но, обо всем по порядку.

<?xml version="1.0" encoding="windows-1251"?> — заголовок XML-файла. Должен идти строго с начала файла. В нем прописана версия используемого XML-языка и кодировка документа. Я как правило работаю в windows-1251 (пока так удобнее), но, по идее UTF-8 лучше.

- корневой элемент документа (можно придумать свое имя). Атрибуты:
  • Lang - язык документа. Нужен для создания мультиязычных шаблонов.
  • Id - идентификатор текущего раздела.

<lang_table> - таблица языков, используемых на сайте.
- блок элементов навигации:
- блок основной навигации (основная структура сайта):
<item id="0" parent_id="0" is_published="1" section="1"> - элемент структуры сайта. Атрибуты:
  • Id - идентификатор раздела.
  • Parent_id - идентификатор родительского раздела.
  • Is_published - опубликован ли раздел.
  • Dir - uri-адрес раздела. По нему формируются полные адреса.
  • Section - тип раздела. Используется если необходимо разбить меню на основное и сервисное.

- блок содержимого.
В моей CMS используется модульная структура: все наполнение сайта представляет собой модули двух видов:
  • Html - текстовый модуль. Статические модули, которые заполняет редактор сайта.
  • Com - модуль-компонента. Динамические модули, которые формируют различные программные модули CMS: новости, статистика, поисковые блоки и т.д.

В XSL-шаблонах есть разметка блоков, в которые можно размещать модули. Для определения блоков я использую простую нумерацию.

CMS при сборке страницы просто выводит в все модули, которые задействованы на странице в виде:
Атрибуты:
  • Id - идентификатор модуля.
    Container - блок-назначение (в каком блоке шаблона выводиться).
    Sorting - порядок вывода в блоке.
    Type - тип:
    • Com - модуль-компонентаю
      Html - текстовый модуль.

    Method - обработчик данных.
    Title - название модуля.
    DTD я практически не использую (лишь в самом общем виде):
    <!DOCTYPE site_page [

    <!ENTITY nbsp " ">
    <!ENTITY sect "§" >
    <!ENTITY copy "©">
    <!ENTITY laquo "«">
    <!ENTITY reg "®">
    <!ENTITY deg "°">
    <!ENTITY plusmn "±">
    <!ENTITY para "¶">
    <!ENTITY raquo "»">
    <!ENTITY times "×">



    <!ENTITY bull "•">
    <!ENTITY hellip "…">



    <!ENTITY ndash "–">
    <!ENTITY mdash "—">
    <!ENTITY lsquo "‘">
    <!ENTITY rsquo "’">
    <!ENTITY sbquo "‚">
    <!ENTITY ldquo "“">
    <!ENTITY rdquo "”">
    <!ENTITY bdquo "„">
    <!ENTITY lsaquo "‹">
    <!ENTITY rsaquo "›" >
    <!ENTITY euro "€">
    ]>

    Его можно вставить прямо в XML-документ. Сразу после <?xml version="1.0" encoding="windows-1251"?>.

    Подготовка XHML-шаблона


    XSL-шаблон создается на базе XHTML-шаблона (некой типовой страницы сайта). Код XHTML-страницы, при этом, должен быть валидным.

    Рассмотрим по шагам процесс создания шаблона.

    Проверив валидность XHML-страницы своего шаблона, для облегчения собственной работы, обозначьте в нем положение всех динамических блоков:
    • Меню (и других элементов навигации).
    • Информационных блоков страницы - то место в шаблоне, в котором будут выводиться модули сайта.
    • Заголовка/названия страницы.

    Сделать это лучше всего с помощью обычных HTML-комментариев:
    ...


    Администрирование сайта


    ...

    • Начало
    • Новости
    • Разделы


    ...

    Всякие новости

    ...

    Текст

    ...


    Основы описания XSL-шаблонов


    Все файлы XSL-шаблонов имеют следующий вид:
    <xsl:stylesheet version = '1.0' encoding="UTF-8"?>
    <xsl:template match="element">
    данные шаблона
    </xsl:template>
    </xsl:stylesheet>


    Где: <xsl:stylesheet version = '1.0' encoding="UTF-8"?> - определяет тип XML-документа и кодировку. Я использую UTF-8 (не спрашивайте, почему).
    <xsl:stylesheet> </xsl:stylesheet> - начало и конец XSL-документа.
    <xsl:template match="element"> </xsl:template> - начало и конец шаблона для элемента element.

    Шаблоны можно условно разделить на три вида:
    • <xsl:template match="element"></xsl:template> - шаблон, описывающий правила преобразования элемента element. Применяется автоматически ко всем элементам element.
    • <xsl:template match="element" mode="mode1"></xsl:template> - шаблон, описывающий правила преобразования элемента element в режиме mode1. Таким образом можно описать различные правила обработки элементов element.
    • <xsl:template name="template-name"></xsl:template> - шаблон с именем template-name. Не имеет привязки к какому-либо элементу XML-документа.

    Если элементы одного вида могут встречаться в различных частях структуры XML-документа (например, в XML-документе, формируемом системой элемент item используется повсеместно и имеет разное значение), то в шаблоне можно указать "структурный адрес" такого элемента:
    <xsl:template match="navigation/sections/item"></xsl:template>
    При этом, порядок применения шаблонов иерархичный, т.е., сначала шаблон применяется к корневому элементу, а затем, к дочерним, т.е. если мы вызвали обработчик для navigation, то для вызова обработчика для navigation/sections/item нам достаточно указать адрес sections/item.

    Структура папок шаблонов


    Для того, чтобы хранить на одном сайте несколько модулей необходимо как-то продумать структуру их хранения в папкам. При этом, удобнее разбить шаблоны на модули по нескольким xsl-файлам. Такой подход позволит в дальнейшем повторно их использовать при создании новых шаблонов.

    В простейшем варианте можно создать каталог xsl и там все складировать.

    Далее, чтобы внутри этого каталог шаблоны не путались (для каждого шаблона у нас получиться несколько файлов) создадим вложенные каталоги:
    • template_folder - каталог с файлами шаблона. Называть ее можно по имени шаблона, например my_template.
    • dtd - файлы описания основных сущностей. Могут быть полезными.
    • lang - шаблоны сообщений для различных языков (если на сайте используется их используется несколько).
    • mod - шаблоны модулей.

    Нам для начала потребуется создать каталог xsl/my_template и в нем, файл layout.xsl следующего вида:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet SYSTEM "../dtd/entities.dtd">
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="/node()">
    </xsl:template>

    </xsl:stylesheet>

    Где:
    <xsl:template match="/node()"> </xsl:template> - шаблон для элемента /node() (корневого). Вместо /node() можно указать //document, т.к. он у нас являеться корневым узлом.

    Копируем весь XHTML-код внутрь блока <xsl:template match="/node()"></xsl:template>

    Этот шаблон будет автоматически применяться ко всему XML-документу. В нашем случае, XSL-преобразование заменит весь XML-код на XHTML-код вашего шаблона.

    Далее, необходимо в директории XSL создать файл template.xsl (где, template - название вашего шаблона), в котором размещаем следующий код:
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:import href=" my_template /layout.xsl"/>
    </xsl:stylesheet>

    Где:
    <xsl:import href="my_template/layout.xsl"/>
    Директива импорта внешнего XSL-файла (обрабатываеться XSL-процессором) из указанного файла. Путь к файлу указываем относительный.

    Создание шаблона для основного навигационного меню


    Наш предыдущий шаблон не обладает никакой динамикой, т.к. просто заменяет весь выходной XML-документ на код нашего шаблона.

    Следующий шаг - создание шаблона для меню.

    Меню навигации сайта строиться на основе его структуры, представленной в XML-документе в следующем виде:
    <item id="0" parent_id="0" is_published="1" section="1">
    Начало
    /

    <item id="1" parent_id="0" is_published="1" section="1" hit="yes">
    Новости
    news




    Текущий раздел определяется по двум параметрам:
    • Атрибуту id у корневого элемента document - он всегда равен id текущего раздела.
    • Атрибуту hit у элемента item - если таковой имеется, то это значит, мы находимся на "главной странице раздела".

    Соответственно, для того, чтобы вывести меню сайта необходимо создать шаблон для элементов:
    • sections - корневой элемент меню.
    • item - элемент меню.

    При этом, необходимо учесть, что элементы item могут содержать другие элементы item, в том случае, если у раздела есть подразделы:



    1. Создаем в директории xsl/my_template файл navigation.xsl следующего вида:


    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xsl:stylesheet SYSTEM "../dtd/entities.dtd">
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:template match="sections" mode="global_menu">
    </xsl:template>

    </xsl:stylesheet>


    2. Вставляем в шаблон код нашего меню из файла layout.xsl:



    <xsl:template match="sections" mode="global_menu">

    </xsl:template>


    3. …а на его место в файле layout.xsl вставляем вызов нашего шаблона меню:



    <xsl:apply-templates select="navigation/sections" mode="global_menu"/>


    Где:
    select="navigation/sections" - относительный (относительно текущего) путь-адрес элемента. При этом, будут обработаны все элементы navigation/sections.

    mode="global_menu" - используем шаблон с режимом global_menu. Это нам нужно на тот случай, если нужно будет выводить еще и сервисное меню, отдельно, или "хлебные крошки", или что-еще другое на основе одной и той же ветки навигации.

    4. Плюс, добавим в файл layout.xsl директиву импорта файла шаблона navigation.xsl:


    <xsl:import href="navigation.xsl"/>

    5. Далее, создаем в файле navigation.xsl еще один шаблон, для обработки пунктов меню:



    <xsl:template match="item" mode="global_menu">
    <xsl:call-template name="href_attribute"/>
    <xsl:value-of select="title"/>


    </xsl:template>


    Где:
    <xsl:call-template name="href_attribute"/> - вызов шаблона по имени. При этом шаблон не имеет привязки к элементу, т.е. вызывается произвольно.

    <xsl:value-of select="title"/> - вставка-вывод значения элемента title текущего элемента. Если в параметре перед именем элемента поставить символ @ - выводиться будет значения атрибута текущего элемента.

    6. Немного изменяем шаблон sections:



    <xsl:template match="sections" mode="global_menu">

    </xsl:template>


    Где:
    <xsl:apply-templates select="item" mode="global_menu"/> - обработка всех элементов item элемента sections. При этом, элементы item самих элементов item (sections/item/item) обрабатываться не будут, т.е. выводиться только один уровень меню разделов.

    Мы вынесли обработку элементов item (пунктов меню) в отдельный шаблон. При этом, в нем мы добавили еще и вызов другого шаблона: <xsl:call-template name="href_attribute"/>

    Этот шаблон будет формировать нормальные uri-ссылки для элементов нашего меню. О нем немного позже.

    7. Теперь нам необходимо доделать меню,


    чтобы оно учитывало, какой раздел является текущим. Для этого нам придется добавить условную обработку в наш шаблон элемента item:

    <xsl:template match="item" mode="global_menu">
    <xsl:choose>

    <xsl:when test="descendant-or-self::*/@id = /node()/@id">
    <xsl:value-of select="title"/>
    </xsl:when>

    <xsl:otherwise>
    <xsl:call-template name="href_attribute"/>
    <xsl:value-of select="title"/>

    </xsl:otherwise>
    </xsl:choose>

    </xsl:template>


    Здесь мы сталкиваемся с новой конструкцией:
    <xsl:choose>
    <xsl:when></xsl:when>
    <xsl:otherwise></xsl:otherwise>
    </xsl:choose>

    …которая, собственно, и задает условную обработку XML-элементов. В качестве параметра мы задаем условие: <xsl:when test="descendant-or-self::*/@id = /node()/@id">

    В нашем случае это условие равенства атрибутов ID у корневого элемента (document) и текущего элемента (item), которое и определяет, является ли элемент текущим.

    Внутри блока <xsl:when></xsl:when> располагается то, что выводиться в случае выполнения условия. В блоке <xsl:otherwise></xsl:otherwise> - если условие не выполняется.

    8. Теперь, разберем шаблон href_attribute:



    <xsl:template name="href_attribute">
    <xsl:attribute name="href">
    <xsl:text>/</xsl:text>
    <xsl:for-each select="ancestor-or-self::item">
    <xsl:value-of select="dir"/>
    <xsl:text>/</xsl:text>
    </xsl:for-each>
    </xsl:attribute>
    </xsl:template>


    Здесь мы сталкиваемся с инструкцией xsl:attribute. Она позволяет создавать атрибуты для элементов внутри которого она вызывается. В нашем случае мы вызываем ее из элемента a, соответственно, она создаст для него атрибут href, т.е. адрес.

    Инструкция <xsl:for-each select="ancestor-or-self::item"> задает цикл обработки для всех элементов, удовлетворяющих условию. В нашем случае мы выбираем ancestor-or-self::item - ось элементов от корневого элемента до текущего по цепочке. В нашем случае это позволяет выбрать для всей цепочки узлы dir, т.е. построить полный адрес текущего узла-раздела.

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

    UPD:
    Материалы к статье. Собрал из того, что было:
    parser.proc.ru/iso/xslt-1.zip

    В шаблоне все пути прописаны от корня (делал на основе шаблона работающего на реальном сайте) поэтому либо перепишите их на относительные либо запускайте из под Apache.

    В архиве входной XML-документ лежит в /xsl/document.xml

комментарии (100)

  • Спасибо! Интересно изложили..
  • Отлично, как давно я искал такой мануал. Всё по делу и ничего лишнего. Огромное спасибо!
  • Спасибо, хорошая статья!

    Мне во многих проектах доводилось плотно заниматься xsl-трансформацией. За это время накопились несколько замечаний, которые стоит учесть при выборе этой технологии.

    1. тормозит
    Нельзя сказать, что безумно, но если дело доходит до оптимизации скорости ответа сервера — xsl-трансформация окажется одним из самых «узких» мест. Можно писать «правильные» шаблоны и со временем научиться «разгонять» скорость их обработки. Но значительного прироста в скорости это не принесет. В последнем проекте это сыграло решающую роль и пришлось отказаться от xsl вообще.


    2. нет четкой структуры. В случае большого объема кода приходится изобретать и постоянно придерживаться четких правил для вложенности шаблонов. Встроенных средств для этого нет.

    3. верстальщики не знают xsl. Это заметно осложняет последующую поддержку проекта.
    • 1. Для того чтобы не тормозило надо использовать кеширование.
      2. Четких правил придерживаться стоит всегда.
      3. Плохие верстальщики не хотят знать XSL. Хороших можно научить.
      • Согласен по всем пунктам, однако:

        1. действительно это один из способов «разгона», но если страница на 100% динамическая — помогает мало.
        2. это гораздо удобней делать, когда правила встроены в технологию или даже диктуются на уровне синтаксиса.
        3. к сожалению кадровой политикой везде занимались другие люди и большого выбора верстальщиков не было.

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

        И кстати, «я за наших!» т.е. мне нравится xsl и интересно было с ним работать. Но есть моменты на которые необходимо обратить внимание при выборе технологии. Т.к. отказаться от xsl, когда проект уже практически готов будет очень сложно.
        • Плох тот верстальщик, который не хочет научиться большему ) Я вобще против такой профессии. Более уместно будет Front-End Web Developer, тут даже само название обязывает знать больше чем просто верстка. )

          А в плане технологий - все просто и неоднократно оговорено. Технологию надо выбирать в зависимости от нужд проекта, а не в зависимости от самой технологии. Это как "php-программист" )
          • не нужно решать за людей чему им учиться.
            человек имеет право решать - нужно ему это или нет.

            знания xsl скорее нужны девелоперу, и как результат, грамотного верстальщика может просто не оказаться под рукой когда он нужен ( девелоперы обычно заняты на 120%)
            • Не знаю что хуже, программист верстающий HTML, или верстальщик ковыряющийся в SQL. Беда коль сапоги начнет тачать пирожник, а пироги печи сапожник.
              • Именно поэтому есть такая профессия - верстальщик
            • Во-первых вы невнимательно прочитали мой комментарий. Девелоперы тоже делятся на Front-End и Back-End.

              Во-вторых вы видимо не имели дело с XSLT. Никто при разработке больших проектов не будет верстать все в статике, а затем просить "девелопера" переводить все на XSLT-шаблоны. Это бессмысленая трата времени.

              XSLT имеет самое прямое отношение к верстке и является частью Front-End'a.

              Кроме того, я ни за кого ничего не решаю, каждый сам себе Прокруст. И вы, пожалуйста не решайте, что мне говорить, а чего не стоит.
              • Никто при разработке больших проектов не будет верстать все в статике, а затем просить "девелопера" переводить все на XSLT-шаблоны.

                Мне кажется это разного плана задачи - одно дело отладить сверстанный дизайн под все браузеры, другое дело связать с бекэндом.
              • На счет больших проектов - вы не поверите что там происходит :) Там и HTML в базу кладут и еще похлеще вытворяют. Знаю, участвовал и да сих пор общаюсь с людьми, которые делают проекты для Билайна, Боинга и других крупных корпораций.

                Далее, "не будет верстать все в статике, а затем просить "девелопера" переводить все на XSLT-шаблоны".
                Простите, а Вы стадию прототипирования начисто игнорируете? И frontend и backend "по-живому" делаете? Не хочу в это верить, боюсь я таких людей :)

                Ну и по последнему - я не указываю Вам, тут все выражают мнение. Обратите внимание, я как раз и писал о том что каждый выбирает сам. Это Вы пытаетесь указать "Плох тот верстальщик, который не хочет научиться большему", что как я вижу есть принуждение.

                P.S. XSLT нужно применять осторожно и с умом. И только когда это оправдано.
                • Я хорошо знаю что происходит в больших проектах, так как принимаю в них непосредственное участие. И думаю что все зависит от компании и работников.

                  Backend'ом я не занимаюсь, а frontend сразу начинаю с XSLT, это гораздо удобнее, по крайней мере в моей компании. Прототипирование здесь не причем. Это более ранняя стадия.

                  Если вы считаете что мои слова - принуждение, то скажу более глобально. Плох тот разработчик, который не хочет учиться чему-то новому. Разработчика, который считает что он уже все знает, надо гнать в шею.

                  По поводу технологий согласен и повторюсь. Цель определяет средства, а не наоборот.
                  • Можите привести примеры крупных проектов с примением XSLT?
                    • Давайте в личке продолжим, ок? )
                    • Почти все проекты Яндекса, чего далеко ходить.
          • Правильная позиция. Нет смысла плодить узких специалистов. Пусть специалист будет узким пока он ничего не умеет. А с таким подходом специалисты не хотят ничему учиться, потому что это не их область, они, видите ли пришли на html-верстальщиков.
        • Я верстальщик с двухлетним стажем, очень жажду изучить XSL, но на работе так заваливают рутино, что времени вообще ноль.
          • Почитайте http://www.zvon.org/xxl/XSLTutorial/Outp…
            Мне хватило недели, чтобы разобраться.
            • хм, забавно... через полгода работы с XSLT 2.0 (Saxon) я понял что я бог и могу делать на нем все, через год до меня дошло что я не знаю ключевых моментов даже XSLT 1.0, через полтора я каждый раз сомневаюсь в разумности использования той или иной конструкции, и каждый день сулит все новые и новые открытия, и день когда я скажу что разобрался с XSLT по-моему никогда не наступит...

              впрочем, это исключительно проблемы моего собственного мозга, у вас конечно все получится намного быстрее)
            • Уже ничего на этом сервере нет, на главной высвечивается - "It works!", надеюсь что временно.
              • Смотрю - все есть... идите прямо по ссылке.
                • Заработал сайт, это радует :)
        • если страница на 100% динамическая

          1. тогда узкое место будет скорее всего не в трансформации
          2. скорее всего можно будет грузить динамику httpRequest'ом и рисовать javascript'ом на закешированой :) после преобразования болванке (например корзина и т.д.)
        • не знаю, что у вас за шаблоны, но свои я оцениваю как достаточно сложные (потому как результирующее DOM дерево после формирования еще раз обрабатываеся внутри xslt, но и не только) и формирование документа из моего шаблона занимает от одной до четырех сотых секунды. шаблоны все в распарсенном состоянии хранятся в памяти, системы - suse linux 10.1 на pentium d 2.8 GHz и mac os x 10.5 на core 2 duo 2.33 GHz, везде последние libxml2 & libxslt
    • Верстальщики не знают ничего кроме верстки, всему надо учить, и лучше один раз научить XSLT.
  • спасибо за статью!
    было бы очень удобно скачать все файлы статьи одним архивом... хотя бы открыть в любимом xslt-редакторе (подсветка, навигация и т.п.)
    • Собрал из того, что было:
      http://parser.proc.ru/iso/xslt-1.zip

      В шаблоне все пути прописаны от корня (делал на основе шаблона работающего на реальном сайте) поэтому либо перепишите их на относительные либо запускайте из под Apache.

      В архиве входной XML-документ лежит в /xsl/document.xml
      • оу, даже с картинками =)
        many thanks!
      • Поместите это в конце статьи, чтобы людям не пришлось искать в комментариях.
  • Спасибо, интересно, подробно и познавательно! Классная статья.
    Но только одно замечание - вы забыли, как и все сторонники хслт, о самом важном вопросе, а именно: "нахрена козе баян?"
    Система тратит время как на составление хмля, так и на его же парсинг, а где же плюсы? Каково принципиальное отличие от тех же пхпшных шаблонизаторов, от того же популярного смарти?
    • Цель статьи ответить на вопрос: Как? а не Зачем.
    • Иначе получиться очередной холивар. Всегда должен быть выбор. Я свой сделал. Однако, я не собираюсь никого убеждать в оптимальности своего выбора. Просто он такой.

      По тем же причинам я использую
      - Parser, а не PHP
      - Xara, а не Corel или Illustrator.
      ... список можно продолжать дальше.
    • Не смешите, хотите сказать, что смарти отличается большой производительностью?
      • Грамотно настроенная и на небольших проектах - да. Возможно и на средних нормально покажет себя.
    • >Каково принципиальное отличие от тех же пхпшных шаблонизаторов
      Главное отличие в том, что XSLT является w3-стандартом, поддерживаемый всеми ведущими разработчиками. А смарти - это всего навсего одна из многих надстроек в PHP, которая даже в рамках PHP не рассматривается как стандарт.
    • Каково принципиальное отличие от тех же пхпшных шаблонизаторов, от того же популярного смарти?


      приниципальное - трансформации можно гонять на клиенте а не на сервере ;)

      хотя на практике мало кто этим пользуется...
      • Возникает куча проблем.
        - устаревшие броузеры не подцепят.
        - если делать тупо в лоб, возникают траблы с поисковыми машинами
        - Операция ресурсоемкая, на слабых комп. торможение заметно.
        - XSLT в броузерах неполноценное. Навскидку - траблы с disable-output-escaping.
        • ну... я же про принципиальное отличие, а не про плюсы/минусы/проблемы ;)
          • Вы - идеалист :)
            Мне кажется когда броузеры научаться парсить XSLT(2), дискуссия XSLT ver php-шаблонизаторы потеряет смысл.
    • XSLT дает очередную возможность отделения мух от котлет, притом полную, сам стараюсь работать только с xslt, простейший пример: есть XML и его можно вывести как заполненную форму так и просто как два дива, плюс при выводе вида не надо заморачиваться с экранированием спец символов.
      P.S. Русские примеры xml/xpatch/xslt немного посеченгная версия английской версии. И, по хорошему, кучу нюансов эти примеры не отражают, искал в свое время некоторые решения по маленьким кусочкам, так что куча документации - это громкие слова
  • спасибо, за статью, есть вопросик - почему именно шаблоны lang, как мне кажется в случае локализации лучше всего поможет именно dtd?
    • В настоящее время у меня для разных языков используются специальные xsl-таблицы. С ними проще работать чем с DTD. Кроме того, я сейчас пытаюсь перевести все на динамические XSL-шаблоны, которые формируются индивидуально для каждого пользователя.
      • не вижу особых проблем написать в dtd <!ENTITY site.about.title "Привет мир">,
        а потом в шаблоне <xsl:text>&site.about.title</xsl:text>, а каким образом Вы делаете вызов разных таблиц для разных локалей, через call-template или apply-template?
        • Можно. Попробую. Но будут проблемы, если мне нужно вставлять что-то вроде:

          184209, Мурманская обл., г. Апатиты, ул.Такаято,
          <br />
          <a href="mailto:mail@domen.ru"><a href="mailto:mail@domen.ru">mail@domen.ru</a></a>

          Насчет вызова разных таблиц - напишу в следующей статье. Там много тонкостей.
          • НЛО прилетело и опубликовало эту надпись здесь.
  • <xsl:template match="/node()"> </xsl:template> - шаблон для элемента /node() (корневого). Вместо /node() можно указать //document, т.к. он у нас являеться корневым узлом.

    меня немного пугают используемые вами xpath-выражения, если вы хотите взять корневой элемент почему бы не сделать это хотя бы так match="/document", во втором случае (//document) процессор не остановится на первом элементе, а продолжит бежать по всему дереву в поисках элемента document, а оно вам надо? старайтесь использовать // как можно реже, вы теряете в производительности
    • У меня используется /node().
      //document я указал, т.к. /document подглючивал у меня сегодня в IE.

      Насчет // - согласен.
      • лично для себя я считаю хорошей практикой начинать с корня

        <xsl:template match="/">
        <xsl:apply-templates />
        </xsl:template>

        а дальше уже обращаться ко всем элементам самым простым способом

        <xsl:template match="document">
        ...
        </xsl:template>

        но это уже каждый сам для себя решает, что хорошо, а что плохо)
        • Раньше я так и делал, но когда схема документа "разрослась" стал называть все, чтобы было проще самому читать свой код.
  • Подходит для относительно небольших страниц, на которых в основном статический контент. Если что-то больше - лучше использовать native php или шаблонизатор на компилируемом языке, например Си.
  • Идеология очень хороша, практикую уже 2 года. Но есть проблема: шаблонизированные формы, т.е. элементы form, которые обёрнуты в шаблон XSLT. Так вот, при отправке методом POST, поля такой формы не передаются серверу из Opera. Из FF и IE передаются, но исключительно в UTF-8, какой-бы чарсет ни был установлен.
    Если кто-нибудь разобрался с этим, пожалуйста, поделитесь решением.
    • Странно. Попробуйте зайти на http://parser.proc.ru/forum/ (там все на XSLT)
      Я оперой давно не пользуюсь, поэтому скатать четко не могу, но проблем быть не должно.
    • Решение есть такое:
      <xsl:output method="xml"
      ...
      />
      Именно из-за этого я делаю один шаблон для Оперы и еще один для остальных браузеров.
      Ну а вообще в Опере 9.5 это уже поддерживается.
  • Некоторые XPath-селекторы несколько нечеткие. Например, node() совпадает в т. ч. и с текстовыми узлами и выражение /node() выглядит как если бы вы хотели выбрать либо элемент, либо текстовый узел. Из контекста же ясно, что имеется в виду только элемент, тем более, что текстового узла в руте и быть не может. Возможно, разумнее использовать более узкий селектор *.

    В descendant-or-self::*/@id = /node()/@id, как мне кажется, вся часть descendant-or-self::*/ лишняя, достаточно @id = /node()/@id, т. е. @id = /*/@id. А я так бы бы еще и переменную завел

    <xsl:variable name="current-section" select="/*/@id" />

    и сравнивал бы так: @id = $current-section.

    Кстати, ведь браузеры по уму должны уметь отображать произвольный XML, если им дать соответствующий CSS для него. Т. е. насколько я понимаю, можно, во всяком случае, теоретически, не втискивать страничку в прокрустово ложе XHTML, а писать XML, точно отображающий структуру сайта. Не делать меню в виде ul id="main-menu", а использовать свой тэг <menu> и задать ему в CSS все те опции, которые сейчас для #main-menu указаны. Единственное ссылки и формы непонятно, как делать, хотя, в принципе, ничто не мешает смешивать свои теги со стандартным XHTML, разделяя их по namespaces.
    • насчет node() я писал немного выше - я не утверждаю, что так и надо. Просто мне пока так удобнее читать: выбираем корневой узел.

      Про <xsl:variable name="current-section" select="/*/@id" /> я постараюсь поговорить позже. Это действительно удобнее. Просто, нужно будет еще объяснить переменные XSL.

      Насчет ul id="main-menu" - эта конструкция более валидная, чем <menu>
      • Возможно, удобнее читать, но вы ведь не узел на самом деле выбираете, а элемент. А элемент — *. Если бы документ был такой

        <sample>
        Some text <some element>some more text</some element>
        </sample>

        то селектор /sample/node()[1] выбрал бы текстовый узел “Some text” (с пробельным материалом вокруг). Когда видишь node(), это читается как «будем работать с текстом и элементами». У вас же работа с текстом не подразумевается.
  • В чем преимущество парсинга при помощи браузера? А в том, что пользователь один раз загрузит xsl-шаблон для страницы (т.е. по сути всю разметку) и потом будет подгружать только данные из xml. Работает это очень и очень быстро.

    Минуса два. Первый - это то, что постоянно сталкиваешься с тем, что в каком-нибудь браузере что-то из xslt не реализовано. Конечно, чаще всего этим браузером становится Опера:) И второй минус - Яндекс не индексирует сделанные таким образом сайты. Т.е. бот заходит, но не индексирует. В чем проблема - пока не знаю. Буду очень признателен, если кто-нибудь поделится своим опытом в этой связи.
    • 1. скорее всего, яндекс, видя чистый xml-документ, не может определить, что у вас там будет title, h1 и т.д.
      2. то что читает бот, не есть то, что видит пользователь. т.е. у вас в каждом xml может быть роман "Война и мир" но после xsl-трансофрмации в итоговую страницу ничего этого не войдет.

      Это нормальная политика для поисковика, не давать себя спамить и т.д. для поддержания высокого качества выдачи.
      • Дак в том-то и дело. Что я сделал выходной xml-файл так, что он выглядит как обычный html со всеми head, body. Ни одного не-html-ного тега нет. Только в начале да подключается xsl.
        Т.е. я сделал тупо текстовую версию сайта со всеми ссылками, переходами. Гугл захавал - я смотрю в кеше и вижу то, что и задумывал. Но вот яндекс, зараза...
        • А namespace прописан?
        • Возможно дело в Content-Type, Яндекс не хочет брать text/xml? Но без него браузер не будет правильно обрабатывать.
        • Спросите у Яндекса напряму. Без их строки поиска :)
        • Да вопрос задавался еще черт-те когда. А ответить соизволили как раз сегодня. Таки да, не поддерживает их робот Content-Type: text/xml.
          Но зато они разрешили делать подмену заголовка специально для их робота - надо будет на стенку в рамочке повесить как официальное разрешение на клоакинг от яндекса:)
  • Спасибо за статью, будем ждать продолжения :)

    не в тему:
    когда уже Хабр будет код подсвечивать и форматировать, а?
  • Огромное спасибо за отличнейшую статью. Сам использую XSLT вот уже 3-й год весьма нравится. весьма быстро и удобно. Но мало еще народа переходит в сайтостроительстве на него, я надеюсь что после таких статей наших прибавится.
  • Как с помощью xslt вывести список в два столбца ?
    например есть список:
    <list>
    <item>1</item>
    <item>2</item>
    <item>3</item>
    <item>4</item>
    <item>5</item>
    <item>6</item>
    </list>
    Как его преобразовать в такое:
    <table>
    <tr>1<td></td><td>4</td></tr>
    <tr>2<td></td><td>5</td></tr>
    <tr>3<td></td><td>6</td></tr>
    </table>
    ???
    • использовать count(list/item) и в зависимости от этого строить нужное кол-во столбцов и строк в них.
    • Если вам нужны _визуальные_ столбцы, может, лучше использовать CSS и свойства float+width?
    • <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:template match="/list">
      <table>
      <xsl:apply-templates select="item[count(preceding-sibling::item) <= count(following-sibling::item)]" mode="tr"/>
      </table>
      </xsl:template>

      <xsl:template match="item" mode="tr">
      <xsl:variable name="pos" select="ceiling(count(../item) div 2)"/>
      <tr>
      <xsl:apply-templates select="." mode="td"/>
      <xsl:apply-templates select="following-sibling::item[$pos]" mode="td"/>
      </tr>
      </xsl:template>

      <xsl:template match="item" mode="td">
      <td><xsl:value-of select="."/></td>
      </xsl:template>

      </xsl:stylesheet>
      • вместо "<=" конечно же нужно поставить "&lt;="
    • XML:



      1
      2
      3


      XSLT:




















      В браузере открывать XML, XSLT-шный файл обозвать transform.xsl и бросить тудаже где файл.xml
      • Когда код посылаете, нажимайте "предпросмотр", т.к. неясно как его хабр отформатирует.
        Символы "<" нужно заменить на "&lt;"
        Сам код лучше обернуть в <code>...</code>.
      • ЕМАЕ! УЖАС КАКОЙ!!!! :D
        Откуда такие грабли?

        XML:

        <?xml version="1.0" encoding="Windows-1251"?&rt;
        <?xml-stylesheet type="text/xsl" href="transform.xsl"?&rt;
        <list&rt;
        <item&rt;1</item&rt;
        <item&rt;2</item&rt;
        <item&rt;3</item&rt;
        </list&rt;

        XSLT:

        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&rt;
        <xsl:output method="html" encoding="windows-1251" doctype-system="http://www.w3.org/TR/html4/strict.dtd" doctype-public="-//W3C//DTD HTML 4.01//EN"/&rt;

        <xsl:template match="/list"&rt;
        <html&rt;
        <body&rt;
        <table&rt;
        <xsl:for-each select="item[(position() - 1) mod 2=0]"&rt;
        <tr&rt;
        <td&rt;<xsl:value-of select="./text()" /&rt;</td&rt;
        <td&rt;<xsl:value-of select="following-sibling::item/text()" /&rt;</td&rt;
        </tr&rt;
        </xsl:for-each&rt;
        </table&rt;
        </body&rt;
        </html&rt;
        </xsl:template&rt;

        </xsl:stylesheet&rt;
        • Господи!!!!
          Почему так криво то??? Какая нафиг подсветка синтаксиса, если оно простое то схавать не может?!!! Охренеть можно.... Раз в 5 минут и так криво!!! :D :D :D Я понимаю что надо предпросмотр юзать - но некогда мне - быстро написал пример и отправил.... Блин! Господа владельцы сайта, давайте я вам дам код для текстарии?? :D
        • Это решение задачи в другом направлении, где будет на выходе получаться
          1 - 2
          3 - 4
          5 - 6

          Требовалось же
          1 - 4
          2 - 5
          3 - 6
          что сложнее
          • ага, недочитал - пример не посмотрел... но можно тоже на фориче рализовать в принципе
    • В коде много переменных, но за счет них он работает в IE.

      <xsl:variable name="totalItems"><xsl:value-of select="count(//list/item)"/></xsl:variable>
      <xsl:variable name="totalItemsHalf"><xsl:value-of select="$totalItems div 2"/></xsl:variable>

      <xsl:template match="//document">
      <html>
      <body>

      <table border="1">
      <xsl:apply-templates select="//list/item" mode="list"/>
      </table>

      </body>
      </html>
      </xsl:template>

      <xsl:template match="item" mode="list">
      <xsl:variable name="cur">
      <xsl:value-of select="position()"/>
      </xsl:variable>
      <xsl:variable name="next">
      <xsl:value-of select="$totalItemsHalf + $cur"/>
      </xsl:variable>
      <tr>
      <xsl:choose>
      <xsl:when test="position() > $totalItems div 2"></xsl:when>
      <xsl:otherwise>
      <td><xsl:value-of select="text()"/></td>
      <td><xsl:value-of select="parent::*/item[position() = $next]/text()"/></td>
      </xsl:otherwise>
      </xsl:choose>
      </tr>
      </xsl:template>


      А так, если использовать CGI-склеивание XML с XSL то код получиться короче
      • И смысл использования трансформации на клинете, если придется так извращаться? :)
        Если трансформер не поддерживает стандарт, то не надо его использовать, это будет мучение одно.
        • Я привел пример для тех, у кого нет возможности по быстрому настроить CGI трансформацию. Этот пример можно просто загрузить в браузер и посмотреть.

          Для рабочих проектов можно использовать все средства XSLT. В этом случае код уменьшается в разы и легче читается.

          Я не говорю, что нужно использовать трансформацию на клиенте. Для этого пока нет достаточной поддержки со стороны браузеров.
    • подсказка:
      match="item[position() div 2 = 1]"
    • http://www.artlebedev.ru/tools/technogre…
    • о-о-о... что то это мне напоминает одно тестовое задание :)
      я прав?
  • XSLT на фронтенде пока зло. На РИТе помнится было смешно, когда про это рассказывал Кудинов (вроде).
    Забили помидорами под корень.
    • А под фронтендом вы здесь подразумеваете клиент/браузер я так понимаю... чтобы почувствовать зло его ведь надо вкусить не так ли?.. вы его вкусили?.. надеюсь что нет. Мне, вот, повезло меньше, я вкусил, но, должен сказать, зло оказалось не таким уж противным на вкус))

      Взять, к примеру, проблему индексации "сайта с XSLT на клиенте" поисковиками, проблема? согласен. А как насчет бэк-офисов, интранет-систем? если вы решаете задачу в этих рамках, то проблем с индексацией у вас уже нет, потому что она там не нужна. Видите, одним злом сразу стало меньше. Нужно просто поискать правильную сферу применения.

      Что касается РИТа, то я на нем не был, но я смотрел запись этого доклада, мне он смешным не показался, мне он показался очень любопытным. Интересно, а как вы оцениваете ваши собственные доклады, уважаемый Flack?
      • Шо ж вы серьезный-то такой.
        Нет, я неделаю интранет-проектов. Не интересно мне как-то. Так что для меня неиндексируемая технология не существует, уж извините :)

        Я говорил не про доклад, а про обсуждение.
        • ну как можно обвинять в серьезности человека с такой аватаркой))

          просто ваше утверждение звучит уж очень безапеляционно, мне захотелось немного его расшатать, и я не являюсь адвокатом Кудинова, но фраза показалась мне не очень дружелюбной)

          P.S. Ты же сам докладчик, Лёха) прикинь, я напишу: было смешно, когда про это рассказывал Рыбаков (вроде)................ без обид)
          • Я всегда стараюсь рассказывать, чтобы было смешно
            • все, уложил на лопатки, я сдаюсь)
    • я рассказывал, и сам же сказал что делать этого не стоит (если ты не делаешь админку под себя или интранет) и объяснил почему.
      а вопрос этот обсуждается и сейчас, а толковой информации как небыло так и нет.
      • Вопрос исключительно в удобстве: каму как. Мне удобно. Многим нет. Раньше тоже считал это бредом, пока не "попробовал". Теперь у меня вся CMS-ка работает на XSLT: и админка и сам сайт. При этом, алгоритм "сборки" для обоих частей CMS един.
    • и не на РИТе, а ClientSide
      • На Рите, на Рите.
        • хм... интересно :)
          как хоть назывался? и кто читал? а то Кудинова я в докладчиках не нашел. или это было в 2007?
  • Продолжение
  • Хех… хабр весь исходный код поел((
    Fade, не дадите ссылку на эту статью на другом сайте, где такой беды не приключилось?)
Только авторизованные пользователи могут оставлять комментарии. Авторизуйтесь, пожалуйста.