XML - статьи


Реализация XSL-преобразований при разработке сайтов с XML-наполнением страниц


Владимир Шрайбман ()

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

  • Возможности XSL
  • Технология XSL обладает многими положительными свойствами, но на некоторые моменты следует обратить особое внимание. Поскольку этот язык имеет синтаксис XML-разметки, он часто воспринимается как простое и относительно небольшое по своим возможностям средство. Тем не менее, XSL обладает рядом качеств, присущих традиционным языкам программирования. В нем присутствуют переменные, которые, несмотря на сильно ограниченный способ использования, бывают крайне необходимы в роли констант или при передаче параметров в XSL-шаблоны. Помимо средств навигации по XML-документу в языке имеются операторы ветвления и итерации, присутствует небольшой набор функций для работы со строками и числами. Строго говоря, функции являются частью стандарта XPath, но ввиду его тесной связи с XSL, имеет смысл вести речь только об XSL, не заостряя внимание на том, какому именно языку фактически принадлежит определенное средство.

    Сильной стороной XSL является то, что преобразование может состоять из нескольких отдельных листов стилей, и даже быть представлено в виде их иерархии. Это позволяет использовать модульный подход к разработке трансформаций, где шаблоны XSL аналогичны процедурам языков программирования. Мы можем скрывать их реализации, размещая шаблоны в отдельных листах стилей, а также подменять одни модули другими без изменения и доработок остального кода проекта. При обращении к шаблонам разработчикам нет необходимости задумываться о том, кто и как реализовывал соответствующие преобразования. Это имеет особое значение, если написанием кода занимается не один человек, а целая команда.


    Реализация модульного подхода в XSL обладает еще одним важным качеством – полиморфизмом, то есть шаблон, созданный в одном листе стилей, может быть переопределен в другом, что позволяет варьировать его действие на различных уровнях иерархии кода проекта.

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



  • Использование XML






  • Как известно, XML – это язык описания данных, а всякая Интернет-страница независимо от ее структуры и содержания является некоторой информацией. Поэтому, если абстрагироваться от вопросов визуального оформления, каждую страницу можно рассматривать как иерархию вложенных друг в друга, логически относительно независимых информационных объектов. Каждому такому объекту можно сопоставить некоторый XML-элемент, с произвольно выбранным разработчиком именем тэга. Весь же набор данных будет представлять собой древовидную структуру XML-документа.

    Например, объект верхнего уровня “страница”, соответствующий корневому элементу, включает в себя объекты: “шапка” с логотипом, “навигация” или “меню”, которое в свою очередь состоит из отдельных пунктов, и “основная часть” – собственно то, ради чего эта страница создавалась и чем она отличается от всех прочих разделов сайта. И так далее до нужной степени детализации.

    Помимо отображаемых информационных объектов очень удобно бывает использовать невидимую часть, содержащую данные, полезные для XSL-преобразований. Это могут быть сведения о посетителе и его правах (гость, зарегистрированный пользователь, администратор и так далее), язык сайта при многоязыковом проекте, режим показа страницы (обычный, для печати без отображения лишних ссылок и баннеров, или режим отладки). Здесь же следует указать выявленные ранее особенности браузера, используемого посетителем, если вы хотите иметь на выходе различный HTML-код. Еще пример полезных данных – это сценарий действий в случае какого-то многоэтапного процесса, такого как регистрация на сайте, требующая нескольких последовательных шагов, начиная с указания имени пользователя и его адресов и заканчивая его кредитной карточкой, или акция задания вопроса службе поддержки (как это сделано на сайте компании Microsoft). Эту же информацию на стадии отладки можно разместить в качестве комментариев в конечном HTML-коде, тогда она будет доступна сотрудникам, занимающимся тестированием.



    Следует отметить, что разные страницы сайта обычно имеют до определенной степени похожую структуру. Тогда их общие части должны быть размечены одинаковыми средствами, то есть одинаковыми XML-элементами и наборами атрибутов. Фактически в рамках одного проекта имеет смысл разработать некоторый постоянный язык разметки данных со значимыми именами тэгов и атрибутов и придерживаться его. При этом совершенно не обязательно проводить всю разработку используемого языка сразу от начала и до конца, ее можно делать по мере необходимости, тем более что, как показывает практика, эта разметка постоянно дорабатывается и пополняется. Нет большой необходимости и в формализации такого языка при помощи DTD или XML-схем, поскольку он предназначен для использования только внутри проекта, и уже достаточно знания его командой разработчиков.

    Теперь несколько слов о том, как получить соответствующим образом размеченные данные. Если разрабатывается проект с динамическими страницами, XML-представление должно генерироваться налету. Тогда здесь доступна вся гамма средств. Это могут быть XML-ориентированные базы данных, которые сами создадут интересующий вас код, CGI-программы, скрипты ASP (ASP.NET) или PHP, Web-приложения, написанные на языке Java. В случае полноценной программной реализации каждому информационному элементу страницы может соответствовать объект, черпающий необходимые данные в надлежащем источнике (например, реляционной СУБД) и имеющий метод для генерации по этим данным желаемой XML-разметки.

    Информация может быть и статичной и храниться постоянно в виде XML-файлов. В последствии, когда будет происходить создание HTML-представления страницы, эти файлы будут легко доступны. Поскольку XSL умеет работать одновременно с несколькими XML-документами, какую-то часть данных имеет смысл специально оставлять статичной. Скажем, на странице регистрации пользователя может потребоваться ввести страну, город и область. Если вы хотите предложить посетителю готовый список возможных вариантов, то строки этого списка логично было бы хранить в файле и обращаться к нему средствами XSL на этапе трансформации. Нет никакого смысла генерировать эти строки динамически, а включать их в текст XSL-документа было бы не правильно, потому как вы имеете дело с данными, возможно изменяющимися, а не с оформлением.





  • Реализация XSL-преобразований




  • Пусть уже разработана серверная часть сайта, которая генерирует информационное наполнение страниц в виде XML. Требуется создать листы стилей для визуализации данных, преобразования XML в HTML. Здесь, разумеется, мог бы быть и не HTML, а любой другой подходящий язык, но для определенности рассматривается именно такая разметка, являющаяся к тому же самой распространенной. К написанию XSL-кода предъявляются в точности те же требования, что и при программировании на традиционном языке, а именно повторное использование кода, простота разработки, легкость отладки, минимальное взаимное влияние реализаций отдельных частей проекта и так далее.

    Упомянутая независимость информационных объектов проявляется в их разнородности, то есть если бы пришлось реализовывать страницу программным путем, то указанные объекты были бы экземплярами различных классов. Но каждый класс должен иметь свое собственное представление, не связанное с визуализацией других, то есть каждому отображаемому XML-элементу нужно поставить в соответствие свой XSL-шаблон, создающий необходимый данному элементу HTML-код и вызывающий процедуры для рисования вложенных объектов. Скажем, шаблон, соответствующий корневому элементу XML-дерева каждой страницы, рисует в верхней части экрана логотип, затем выделяет место для строки меню и вызывает создающую это меню процедуру, следом отводит место для основной части страницы и также вызывает шаблон для ее заполнения. При этом заметьте, что контейнер меню не должен знать о том, как устроен код отдельных пунктов, он лишь должен зарезервировать для них место на странице, и в свою очередь пункты меню не должны знать, что находится в иерархии над ними – они должны лишь уметь воспользоваться отведенным для них местом.

    Такое логическое разбиение XSL-преобразования позволяет легко справиться со сложностью кода страницы. В то время как традиционный HTML-код неизменно растет и становится все более запутанным по мере увеличения объема информации, иерархия шаблонов XSL позволяет практически не ощутить это усложнение. Заметно упрощается притирка отдельных элементов страниц друг к другу, тестирование и поиск ошибок в коде, поскольку разработчику не приходится просматривать значительный объем HTML, силясь отыскать нужное место и отделяя интересующую его часть кода от прочих частей. Он может сосредоточиться на относительно небольшой реализации конкретного шаблона, не думая, а в случае командной разработки, возможно, и не зная о том, как реализованы остальные элементы страницы.



    Различные страницы одного сайта, как правило, имеют одинаковую схему размещения своих логических частей, основных информационных объектов на экране. Поэтому шаблоны, отвечающие за разбиение доступного пространства и раскладку информации, или отображающие меню, должны выполнять одну и ту же работу для разных страниц, а значит должны быть написаны один раз. Следует сделать оговорку, что раскладка может зависеть от режима показа информации, например, от того, предназначена ли страница просто для просмотра на экране или подготовлена специально для печати. Но это может быть отражено в XML-данных и использовано соответствующим шаблоном для создания представления, более чуткого к пожеланиям пользователя.

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

    Многие страницы часто содержат дополнительные общие конструкции и блоки: вспомогательную или контекстную навигацию, рекламные вставки, однотипные окошечки и колонки для различных рубрик или одинаковые таблицы, представляющие витрины Интернет-магазина.

    Большое количество сайтов использует разнообразные графические детали, в которых проявляется творчество дизайнеров. Это могут быть сложно устроенные кнопки, ссылки с нетривиальным оформлением, пункты меню, заголовки, разделительные линии и прочие элементы. Фактически различные экземпляры таких объектов отличаются друг от друга небольшим фрагментом текста, адресом гиперссылки и другими подобными сведениями, которые могут быть переданы в качестве параметров. Выделение такого кода в специальные шаблоны не только позволяет использовать его повторно, но и за счет замены многочисленных инструкций XSL на отдельный вызов опять же значительно упрощает для понимания код, отвечающий за соответствующую область экрана.

    Имеет смысл определить шаблоны и целые группы шаблонов, требующиеся не всем страницам, а лишь какому-то их подмножеству, или собранные по какой-то тематике. Так, если при неправильных действиях пользователя или недопустимых значениях, указанных им в полях формы, серверная часть генерирует стандартный информационный объект с описанием ошибки, можно собрать в одном месте обработчики всех возможных типов ошибок.



    Помимо шаблонов в роли процедур для рисования полезно выделить ряд параметров, которые выполняли бы функцию глобальных констант. Например, размеры логотипа и высоту строки меню, место расположения картинок и файлов с JavaScript-кодом на сервере, общий цвет фона и другие значения, которые иногда удобнее подставлять непосредственно в HTML, а не выносить в CSS-листы стилей.

    Таким образом, мы приходим к физическому разбиению XSL-кода на иерархию подключаемых друг к другу модулей. На верхнем уровне этой иерархии находится лист стилей или группа листов, содержащих константы и шаблоны, используемые для визуализации общих информационных объектов основной массы страниц сайта. На средних уровнях – шаблоны, необходимые какой-то их группе. На нижнем – модули, каждый из которых соответствует конкретной странице и реализует шаблоны, необходимые только ей. Здесь у всякой страницы нужно определить шаблон, отвечающий за содержимое основной ее части. В большинстве случаев персональный модуль состоит всего из нескольких шаблонов. Стоит отметить, что они редко вносят что-то новое в дизайн сайта, поскольку обычно их задача состоит в создании HTML-форм, текстов, таблиц, списков, ссылок. Поэтому подготовка XSL-долкумента для отдельной страницы при наличии общих модулей не требует большого количества времени и сил.



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

    Необходимо сделать небольшое пояснение, почему речь идет об иерархии. Именно здесь используется полиморфизм XSL. Мы можем задать реализацию некоторого шаблона и изменить ее для какой-то группы страниц на более низком уровне иерархии. Позже будет приведен пример использования данной возможности. Эффекта переопределения шаблонов можно добиться и за счет правильной последовательности подключения общих модулей к персональному листу стилей страницы инструкциями import, но при этом модель вызова шаблонов по-прежнему останется иерархической.



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

    Если какой-то модуль окажется слишком большим, а они обычно регулярно пополняются по мере разработки, и не удобным для использования, его всегда можно разделить на части, выполняющие ту же самую задачу. Тем не менее, суммарный объем XSL-кода всех листов стилей, принимающих участие в преобразовании одной страницы оказывается достаточно большим, что не может не сказаться на времени работы XSL-процессора. Но как раз в случае применения общих модулей эта проблема легко решается. Наиболее часто используемые листы стилей не должны анализироваться парсером при каждом обращении пользователя к какой-нибудь странице сайта, а должны быть один раз считаны, и находиться в готовом к работе состоянии в оперативной памяти сервера. Тогда XSL-процессор может динамически подключать их к персональным модулям страниц, обрабатываемым уже по запросу пользователя. Это экономит значительную часть времени, требующегося серверу для генерации ответа клиенту. Чтобы организовать преобразование указанным образом, необходимо написать небольшую программную надстройку над любым XSL-процессором, поддерживающим динамический импорт листов стилей, например, над процессором Xalan. Это не требует много времени и сил, а написанная для XSL-трансформаций программа не зависит от конкретного создаваемого сайта, поэтому она может быть многократно использована в различных проектах.

    При правильной организации труда значительно упрощается и командная разработка проекта, поскольку по сравнению с написанием обычного HTML-кода намного эффективнее используется время, внимание и силы разработчиков. Скажем, один человек может модифицировать персональный документ страницы, создавая какой-то ее фрагмент, а в это же время его напарник подправляет шаблон общего модуля, отвечающий за компоновку всех страниц. При этом ни один из них не беспокоится о том, чем занят другой – оба работают одновременно и независимо. Ошибки второго сотрудника практически не влияют на действия первого (если, конечно, он не вырежет вызов шаблона, которым тот занимается), и наоборот – ошибки первого не затрагивают работу второго. Тестировать свой труд они также могут одновременно, и, видя на экране ошибку, по ее расположению легко понимают, кто именно ее допустил. А в то же самое время, третий человек может заниматься переводом той же самой страницы на другой язык, но об этом позднее.





  • Поддержка нескольких версий сайта




  • Предлагаемая архитектура является достаточно гибкой для решения широкого круга задач. В качестве иллюстрации этого можно указать требование иметь одновременно сразу несколько версий сайта. Такая задача возникает далеко не всегда, но имеет право на существование. Например, вы хотите иметь обычную версию сайта для просмотра браузером персонального компьютера, облегченный HTML-код для карманных устройств и специально оформленную версию для отправки на принтер, или же внешний вид страниц должен настраиваться в зависимости от пользователя. Еще примером здесь является некоторый сервис, подобный поисковой системе, который встраивается в другие сайты, перенимая их дизайн, но управляется с одного сервера, то есть поддержка различных версий целиком возлагается на XSL-преобразования.

    Как показала практика, для смены внешнего вида (создания альтернативной версии) сайта требуется переработка относительно небольшого объема кода. В первую очередь это шаблоны, отвечающие за всевозможные графические детали. Также требуется модификация нескольких шаблонов, отвечающих за какие-то блоки экрана или его разбиение на отдельные части. Может потребоваться изменение ряда описанных констант, хранящих сведения о размерах и цветах, плюс переработка CSS-листов и, возможно, картинок.

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

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



    Если говорить о количественной оценке труда, то следует отметить экономичность указанного способа. Автору довелось участвовать в разработке крупного сервиса, встроенного более чем в двадцать других сайтов. Проект содержал свыше сотни страниц и десятки тысяч строк XSL-кода. Для приведения же внешнего вида этого сайта в соответствие с чужим дизайном обычно требовалось изменить не более тысячи строк, то есть необходимо было переработать порядка пяти процентов всего объема XSL-преобразований. Плюс, как уже было отмечено, требовалась коррекция CSS-листов и части картинок. Всего – несколько человеко-дней.



  • Многоязыковая поддержка




  • Еще одна периодически возникающая задача – поддержка сайтом более одного языка.

    Одно из возможных решений этой проблемы состоит в использовании атрибута xml:lang. Однако такой подход обладает рядом недостатков. Хорошо, когда приходится иметь дело с несколькими относительно крупными блоками текста и парой языков. Но если вместо цельного содержания присутствует много маленьких фраз и большее количество языков, XML-документ становится не понятной разметкой информации, а пестрым нагромождением повторяющихся элементов.

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

    Третий недостаток проявляется тогда, когда текстовые фрагменты являются статическими, то есть не обязаны браться из базы данных и, следовательно, не обязаны включаться в динамически создаваемую XML-разметку данных страницы. Примером этого может служить Интернет-магазин, где многие комментарии, названия разделов, надписи на кнопках и так далее не зависят от динамически воспроизводимого ассортимента товаров и, по сути, ближе к элементам оформления, чем к данным. Хранение таких кусочков текста в XML-файлах кажется неоправданным – их место в XSL-коде.



    И последняя трудность. Если при разработке сайта за каждый язык отвечает свой переводчик, то, соединив все языки в одном документе, вы заставите сотрудников конкурировать за право редактировать эту страницу.

    В противоположность использованию xml:lang можно организовать перевод средствами самого XSL аналогично тому, как осуществляется языковая поддержка во многих программах. Ни один из описанных выше листов стилей не должен содержать слов, зависящих от языка. На месте каждого текстового фрагмента следует поместить вызов шаблона, реализация которого вынесена в отдельный XSL-документ, соответствующий какому-то языку, и состоит исключительно в предоставлении необходимого текста. В зависимости от масштабов проекта можно подготовить один документ, содержащий переводы всех кусочков текста и подключаемый к каждому персональному модулю страницы. Можно создать и несколько документов, или даже каждому листу стилей, генерирующему HTML-разметку, предоставить собственный файл с переводами.



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

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

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



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



  • Заключение и предостережение разработчику




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

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

    Во-вторых, хотя каждый разработчик в команде не обязан знать устройства всех шаблонов, требуется заранее договориться и действовать слаженно. В отличие от написания обычного HTML-кода, процесс создания иерархии модулей требует большей продуманности, особенно когда один шаблон используется многими различными страницами. То есть использование XSL требует некоторого предварительного анализа и проектирования. В противном случае код может оказаться не меньшей “кашей”, чем то, что представляет собой HTML-разметка, а разработка превратится в постоянное исправление уже написанного.

    В остальном предлагаемый способ создания сайтов является простым и удобным, и в то же время достаточно мощным, гибким и эффективным. Он прекрасно зарекомендовал себя на практике. Самым существенным ограничением использования XSL видится то, что трансформация занимает некоторое время, и, чем больше исходные XML-данные и XSL-код, тем больше ресурсов для этого требуется. Однако при непрерывно возрастающей вычислительной мощности серверов и при использовании программ, хранящих обработанный код наиболее часто используемых модулей в оперативной памяти компьютера, это время удается сделать вполне разумным, и соответственно указанное ограничение не является слишком серьезным.



    Ссылки на спецификации





    1. Tim Bray, Jean Paoli, C. M. Sperberg-McQueen, “Extensible Markup Language (XML) 1.0”, W3C Recommendation, Октябрь 2000;


    2. James Clark, “XSL Transformations (XSLT) Version 1.0”, W3C Recommendation, Ноябрь 1999;


    3. James Clark, Steve DeRose, “XML Path Language (XPath) Version 1.0”, W3C Recommendation, Ноябрь 1999;



    4. Содержание раздела