Опубликован: 25.07.2012 | Уровень: специалист | Доступ: платный
Лекция 6:

Применение языка преобразований XSLT

< Лекция 5 || Лекция 6: 12 || Лекция 7 >

Обработка с условием

Для применения условий к обработке шаблонов используется одна из инструкций: xsl:if или xsl:choose. Инструкция xsl:if дает простую функциональность if-then, а инструкция xsl:choose поддерживает выбор одного из нескольких возможных вариантов. Сейчас рассмотрим пример с использованием xsl:if, а пример с использованием xsl:choose рассмотрим позже.

В следующем примере необходимо создать код объявления процедур на языке PL/SQL такого вида:

procedure prc_save_record (id in out number, name in varchar2, title varchar2, save_date out date); 
procedure prc_delete_record (id number);
    

Объявление состоит из ключевого слова procedure, имени процедуры, имен и типов параметров. Для каждого параметра может указываться его вид: in, out, in out или без указания его. Представим наше объявление процедур в иерархическом виде в следующем документе:

<?xml-stylesheet type="text/xsl" href="if.xsl"?>
<package>
  <procedure name="prc_save_record">
    <parameter name="id" type="number" in="true" out="true"/>
    <parameter name="name" type="varchar2" in="true"/>
    <parameter name="title" type="varchar2"/>
    <parameter name="save_date" type="date" out="true"/>
  </procedure>
  <procedure name="prc_delete_record">
    <parameter name="id" type="number"/>
  </procedure>
</package>
    
Пример 5.10.

Создадим следующий файл стиля:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
  <xsl:for-each select="package/procedure">
      procedure <xsl:value-of select="@name"/>
      (<xsl:for-each select="parameter">
        <xsl:value-of select="@name"/>
        <xsl:text> </xsl:text>
        <xsl:if test="@in='true'">in </xsl:if>
        <xsl:if test="@out='true'">out </xsl:if>
        <xsl:value-of select="@type"/>
        <xsl:if test="not(position()=last())">, </xsl:if>
      </xsl:for-each>);
      <xsl:if test="not(position()=last())"><BR/><BR/></xsl:if>
  </xsl:for-each>
</xsl:template>
</xsl:stylesheet>
    
Пример 5.11.

Инструкция xsl:for-each здесь используется дважды, причем одна вложена в другую. В первой инструкции просматриваются все процедуры, а во второй выполняется работа над каждым параметром процедуры. В атрибуте test инструкции xsl:if делается проверка на необходимость добавления ключевых слов in, out. Кроме того, инструкция проверки используется для определения того, нужно ли ставить запятую после параметра. Если параметр не последний, что проверяется условием not(position()=last()), то ставится запятая. Функция position() возвращает номер позиции данного контекста в процессе обхода ветвей. Функция last() возвращает общее число позиций. Аналогично проверяется необходимость перехода на следующую строку в конце каждой процедуры.

Инструкция xsl:if используется для выяснения необходимости включения какого-либо шаблона в результирующий вывод. Шаблон, находящийся внутри инструкции xsl:if выполняется только в том случае, когда выражение XPath, находящееся в условии test возвращает истину. При этом аналога команды else языков программирования в XSLT не существует. Для выполнения подобной функциональности надо использовать инструкцию xsl:choose.

Элемент xsl:text служит для вывода текста в результат трансформации. Чаще всего он используется для вывода пробелов, так как без применения xsl:text пробелы будут схлопываться. Атрибут disable-output-escaping позволяет включать и выключать преобразование системных символов вроде знаков больше, меньше, амперсанта.

Инструкция xsl:choose применяется, когда есть необходимость выбора шаблона из нескольких возможных согласно каким-либо условиям. Она содержит одну или более команд xsl:when и может содержать xsl:otherwise. Каждая команда xsl:when содержит в условии test выражение. Если оно является истиной, то выполняется содержимое xsl:when. Порядок условий xsl:when имеет значение, так как результатом всей конструкции будет шаблон под первым истинным выражением. Если ни одно из условий в xsl:when не вернуло истину, в результате будет обработан шаблон под xsl:otherwise.

Например:

<xsl:choose>
<xsl:when test="$a=l">Истина</xsl:when>
<xsl:when test="$a=0">Ложь</xsl:when>
<xsl:otherwise>Некорректное значение</xsl:otherwise>
</xsl:choose>
    

Переменные

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

Они позволяют определить заменяемое значение, которое можно использовать в таблице стилей при трансформации.

<xsl:variable name="Имя переменной" select="выражение" as="тип" />
    

Значение переменной может быть также получено другими путями, например:

<xsl: variable name="a">8</xsl:variable>
    

Или так:

<xsl:variable name="str">
<xsl:value-of select="." /> 
<xsl:variable>
    

Для подстановки значения переменной надо предварить имя переменной знаком $. Процессор XSLT в ходе преобразования присваивает ей значение. Рассмотрим XML-документ из предыдущего примера.

<?xml-stylesheet type="text/xsl" href="variable.xsl"?>
<package>
  <procedure name="prc_save_record">
    <parameter name="id" type="number" in="true" out="true"/>
    <parameter name="name" type="varchar2" in="true"/>
    <parameter name="title" type="varchar2"/>
    <parameter name="save_date" type="date" out="true"/>
  </procedure>
  <procedure name="prc_delete_record">
    <parameter name="id" type="number"/>
  </procedure>
</package>
    
Пример 5.12.

Теперь поставим задачу вывести для каждой процедуры число параметров внутри объявления процедуры. Для этой цели объявим в инструкции xsl:variable переменную amount и присвоим значение count(parameter) - для каждого узла procedure будет считать число узлов parameter. Кроме того при выводе мы собираемся выводить слово параметр в правильном числе (выводим одно из трех: параметр, параметра, параметров). Для этого мы в переменную modulo10 будем записывать остаток от деления amount на 10. А инструкция xsl:choose добавлена для выборки одного из вариантов написания слова параметр.

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <xsl:template match="/">
    <xsl:for-each select="package/procedure">
      <xsl:variable name="amount">
        <xsl:value-of select="count(parameter)"/>
      </xsl:variable>
      <xsl:variable name="modulo10" select="$amount mod 10"/>
      Процедура
      <xsl:value-of select="@name"/> имеет
      <xsl:value-of select="$amount"/> 
      <xsl:choose>
        <xsl:when test="$amount > 10 and $amount < 20"> параметров</xsl:when>
        <xsl:when test="$modulo10=1"> параметр</xsl:when>
        <xsl:when test="($modulo10 > 1 and $modulo10 < 5)"> параметра</xsl:when>
        <xsl:otherwise> параметров</xsl:otherwise>
      </xsl:choose>
      <BR/>
    </xsl:for-each>
</xsl:template>
</xsl:stylesheet>
    
Пример 5.13.

Результат

Процедура prc_save_record имеет 4 параметра
Процедура prc_delete_record имеет 1 параметр
    

Включение внешнего содержимого

Инструкция xsl:import используется для подстановки содержимого другого файла стилей. Ее синтаксис выглядит так:

<хsl:import href="адрес URI" />
    

Она должна быть первым вложенным элементом в <xsl:stylesheet> или <xsl:transform>. В случае наличия конфликтов в стилях, применяются правила, которые записаны позже других.

Аналогом этой инструкции является инструкция xsl:include. Она также должна быть вложена в <xsl:stylesheet> или <xsl:transform>, однако отличие заключается в том, может подставляться в любое место внутри этих элементов и порядок расположения инструкции xsl:include не играет роли. Ее синтаксис выглядит аналогично:

<xsl:include href="адрес URI" />
    

Улучшение функциональности

Есть возможность расширять существующие способности XSLT, например, добавлять скрипты, которые понимает процессор XSLT.

Элемент xsl:function позволяет описывать самые настоящие пользовательские функции. Имя функции записывается в обязательном атрибуте nаmе, (аргументы функции задаются элементами xsl:param, а тело функции - это конструктор последовательности, записанный в содержимом элемента xsl:function. Результатом работы функции будет последовательность, созданная конструктором. Функция может вызываться рекурсивно.

Генерация в C#

Для выполнения преобразования XML-файлов средствами .Net используется класс XslCompiledTransform, являющийся процессором XSLT и поддерживающий XSLT версии 1.0. Метод Load класса загружает и компилирует стиль. Метод Transform выполняет трансформацию.

using System;
using System.Xml;
using System.Xml.Xsl;
using System.Xml.XPath;

namespace LectionXML
{
    class Program
    {
        static void Main(string[] args)
        {
            XPathDocument xpd = new XPathDocument(@"variable.xml");
            XslCompiledTransform xt = new XslCompiledTransform();
            XmlTextWriter xtw = new XmlTextWriter(@"variable.txt",null);
            xt.Load(@"variable.xsl");
            xt.Transform(xpd, null, xtw);
            xtw.Close();
        }
    }
}
    
Пример 5.14.

Подлежащий преобразованию документ XML загружается в объект класса XPathDocument. А класс XslCompiledTransform используется для выполнения преобразования. Объект класса XmlTextWriter применяется для вывода результата в выходной файл.

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

XslCompiledTransform xt = new XslCompiledTransform();
xt.Load("variable.xsl");
xt.Transform("variable.xml", "variable.txt");
    
Пример 5.15.

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

<?xml-stylesheet type="text/xsl" href="variable.xsl"?>
<examples>
  <example>
    <name>Пример 1</name>
    <style>languages.xsl</style>
    <document>languages.xml</document>
    <output>languages.txt</output>
  </example>
  <example>
    <name>Пример 2</name>
    <style>multiple.xsl</style>
    <document>multiple.xml</document>
    <output>multiple.html</output>
  </example>
  <example>
    <name>Пример 3</name>
    <style>if.xsl</style>
    <document>if.xml</document>
    <output>if.txt</output>
  </example>
</examples>
    
Пример 5.16.

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

string style = "";
string document = "";
string output = "";
XmlDocument doc = new XmlDocument();
XslCompiledTransform xt = new XslCompiledTransform();
doc.Load(@"hierarchy_example.xml");
XmlElement examples = doc.DocumentElement;
for (int i = 0; i < examples.ChildNodes.Count; i++)
{
    XmlNode nd = examples.ChildNodes[i];
    for (int j = 0; j < nd.ChildNodes.Count; j++)
    {
        switch (nd.ChildNodes[j].Name)
        {
            case "style": style = nd.ChildNodes[j].InnerText; break;
            case "document": document = nd.ChildNodes[j].InnerText; break;
            case "output": output = nd.ChildNodes[j].InnerText; break;
        }
    }
    xt.Load(style);
    xt.Transform(document, output);
}
    
Пример 5.17.

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

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

Достоинства применения XSLT

  1. Плюсом является то, что шаблоны внешне идентичны выводимому коду, то есть они наглядны и проще для понимания.
  2. Шаблоны легче читать, редактировать, отлаживать и выполнять другие манипуляции. Также относительно легко можно производить поиск нужного участка генерируемого кода в шаблонах. При генерации без применения шаблонов сделать это не так легко.
  3. Язык XSLT уже поддерживает работу с одинарными и двойными кавычками и другими специальными символами, что облегчает применение этих символов в генерации кода.
  4. Можно заново генерировать код, не выполняя перекомпиляцию приложения, а только изменив шаблон.
  5. В отличие от T4 технология XSLT является стандартом, который поддерживают все производители программного обеспечения. Таким образом, при его применении сохраняется независимость от производителя.
  6. Так как XSLT является декларативным языком, в ней гораздо легче простым декларированием можно выполнять многие непростые с точки зрения стандартного программирования операции, вроде прохода по всем узлам документа, их фильтрации, сортировки, подстановки соответствующих значений. Для выполнения их на стандартном языке программирования потребуется трудоемкое программирование, тогда как в XSLT они могут быть реализованы всего лишь несколькими строчками объявлений, а процессор XSLT выполнит все необходимые преобразования. Данный факт является также преимуществом шаблонов XSLT перед шаблонами T4.
  7. Поддерживается разделение метаданных и шаблонов.
  8. Так как шаблоны можно изменять динамически, есть возможность применять XSLT для генерации комбинированно, в сочетании с языками программирования.

Недостатки и ограничения применения XSLT

  1. Шаблоны малоэффективны, когда в генерируемом коде мало стандартных участков и слишком много параметров, от задания которых зависит результирующий код. В некоторых таких случаях шаблон может стать малопонятным и очень сложным для поддержки.
  2. В шаблонах XSLT по сравнению с T4 затруднено программирование и выполнение некоторых операций. Так как XSLT является декларативным языком, в ней трудно реализовывать сложные алгоритмы.
  3. Изучить XSLT на профессиональном уровне не так легко. Выработка навыков его уверенного применения потребует значительных усилий и практики. Поэтому при планировании разработки генератора нужно учесть этот факт.
< Лекция 5 || Лекция 6: 12 || Лекция 7 >
Дмитрий Клочков
Дмитрий Клочков
Россия, Рубцовск
Волков Олег
Волков Олег
Украина, Днепропетровск