IBM®
Перейти к тексту
    в России и странах СНГ [изменить]    Условия использования
 
 
   
    Главная страница    Продукты    Услуги и решения    Поддержка и загрузка    Мой профиль    
Перейти к тексту

developerWorks Россия  >  XML  >

Подготовка к сертификации по XML и смежным технологиям, часть 4: XML-преобразования

Преобразование, поиск, отслеживание и форматирование XML-данных

developerWorks
На предыдущую страницуСтраница 3 из 9 На предыдущую страницу

Опции документа

Обсудить


Выскажите мнение об этом учебном пособии

Помогите нам улучшить содержание


XSLT

Удивительно, что мы можем видеть эти деревья, и даже не удивляемся этому.
Ральф Уолдо Эмерсон

Иногда говорят, что XML не является языком программирования. Это было бы полностью справедливо, если бы не XSLT, собственно XML, представляющий собой полную машину Тьюринга. Это значит, что XSLT можно использовать для выполнения любого набора вычислений, который можно выполнить на современном компьютере.

По существу, XSLT представляет собой систему объявлений событий, которые должны произойти при встрече в XML-документе определенных типов элементов. XSLT не компилируется; вместо этого XSLT совместно с входным XML-документом интерпретируется процессором таблиц стилей, например, Xalan или Microsoft XML Core Services (MSXML). Его использование можно представить в виде математической функции: XSLT( XML ) = выход.

Поскольку в состав названия XSLT входит термин таблица стилей, некоторые из программистов считают, что XSLT в качестве языка программирования не обладает серьезными функциями, это просто что-то вроде каскадных таблиц стилей (CSS). Не имею ничего против CSS, но это слишком простой язык. Хотя XSLT не такой лаконичный, как большинство других языков (во многом из-за необходимости правильности построения), этот язык (в сочетании с XPath, представляющим собой средство поиска и отслеживания древовидной структуры XML и выполнения строковых и математических операций) обладает богатой функциональностью. Можно создавать удивительно элегантный код. Позднее это будет показано при рассмотрении рекурсии.

В следующих разделах будет показано, как использовать XSLT и XPath для извлечения данных из XML-документов. В большинстве примеров эти данные форматируются в HTML.

Преобразования в Web-браузерах

Хотя в следующих примерах кода предполагается, что преобразования выполняются на сервере или в среде, например, XMLSpy, эти преобразования также будут выполняться с минимальными изменениями или вообще без изменений в последних версиях браузеров, поддерживающих XSLT, например, Mozilla Firefox и Microsoft Internet Explorer. Для просмотра в этих браузерах XML-документов требуется директива, подобная следующей, размещаемая в прологе документа сразу после тега <?xml version="1.0"?> в начале входного XML-документа. Задайте атрибуту href соответствующее значение, абсолютное или относительное:

<?xml-stylesheet type="application/xml" href="http://www.ibm/com/xslt/foo.xslt"?>



В начало


Корневой элемент

Корневым элементом или элементом верхнего уровня любого XSL-преобразования, под которым находятся все другие дочерние узлы, является элемент xsl:stylesheet или xsl:transform. Можно использовать любой из этих элементов, они выполняют одинаковую функцию. Можно записать эти элементы в следующем виде:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

или:

<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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



В начало


Элементы шаблона

Элемент xsl:template включает набор правил, применяемых к определенным элементам во входном XML-документе. Каждый элемент xsl:stylesheet или xsl:transform должен иметь хотя бы один элемент xsl:template. Богатые возможности XSLT-программирования обусловлены использованием различных шаблонов в качестве логических модулей, каждый из которых имеет собственное назначение. Элементы шаблона можно переключать с помощью атрибута match или с помощью непосредственного вызова в атрибуте name.

XSLT в XMLSpy®

В XMLSpy XML-документ можно преобразовывать с помощью XSLT:

  1. Загрузите XML-документ в XMLSpy;
  2. Загрузите XSLT-документ в XMLSpy;
  3. Выбрав XML-документ, нажмите клавишу F10;
  4. Для выбора XSLT-документа нажмите кнопку Window, а затем кнопку OK;
  5. Нажмите OK еще раз для вывода результатов преобразования.

Использовать атрибут match довольно просто. При обнаружении шаблона, указанного значением атрибута match, выполняется правило. Например, для индикации каждого элемента dvd, обнаруженного в документе, строкой "Another DVD", можно использовать следующее:

<xsl:template match="dvd">Another DVD</xsl:template>

Вывод результата этого преобразования выглядит примерно так:

<?xml version="1.0" encoding="UTF-8"?>

    <p>Another DVD</p>
    <p>Another DVD</p>
    <p>Another DVD</p>
    <p>Another DVD</p>

Обратите внимание, что для каждого элемента dvd во входном документе имеет одно вхождение правила шаблона. При каждом обнаружении во входном документе элемента dvd вызывается правило.



В начало


xsl:apply-templates и xsl:value-of

Полезно иметь правила шаблона, соответствующие элементам, отличным от элементов dvd. Фактически можно иметь шаблоны, соответствующие другим нужным элементам:

<xsl:template match="price"><xsl:value-of select="."/></xsl:template>
<xsl:template match="title"><xsl:value-of select="."/></xsl:template>

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

Значение атрибута select в теге <xsl:value-of/> в обоих случаях представляет собой шаблон, предоставляющий текстовое значение проверяемого элемента, или узла контекста. Выражение XPath, обозначенное в значениях атрибута select выше символом ".", является собственно осью. Значение атрибута match в теге шаблона также представляет собой выражение XPath. Узел контекста в данном случае задается сопоставлением шаблона. Другой способ задания узла контекста заключается в применении тегов xsl:apply-templates и xsl:for-each.

Предыдущие два тега шаблона не предоставляют только значения title и price; все остальное из входного XML-документа также выводится. Для вывода только значений элементов title и price необходимо еще одно правило шаблона:

<xsl:template match="dvd">
   <p>
      <xsl:apply-templates select="title"/>- $<xsl:apply-templates select="price"/>
   </p>
</xsl:template>

Также добавлено немного HTML-форматирования (тег <p/>). Вывод должен выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?>

    <p>Terminator 2 - $19.95</p>
    <p>The Matrix - $12.95</p>
    <p>Life as a House - $15.95</p>
    <p>Raiders of the Lost Ark - $14.95</p>
 



В начало


Вызов шаблонов

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:template match="dvd">
      <p>
         <xsl:call-template name="Title"/>
         <xsl:call-template name="Price"/>
      </p>
   </xsl:template>
   <xsl:template name="Title"><xsl:value-of select="title"/> - </xsl:template>
   <xsl:template name="Price">$<xsl:value-of select="price"/></xsl:template>
</xsl:stylesheet>

Для удобочитаемости дефис для называния и знака доллара в цене перемещены в именованные шаблоны Title и Price. Рассмотрим тот же вывод, что был создан в предыдущем преобразовании:

<?xml version="1.0" encoding="UTF-8"?>

    <p>Terminator 2 - $19.95</p>
    <p>The Matrix - $12.95</p>
    <p>Life as a House - $15.95</p>
    <p>Raiders of the Lost Ark - $14.95</p>
 

Атрибут name тегов xsl:call-template ссылается на шаблоны, атрибуты name которых имеют такое же значение: "Title" или "Price". Шаблон, вызываемый с помощью его атрибута name вместо атрибута match, называется именованным шаблоном. Именованные шаблоны Named templates учитывают модульное кодирование. Далее будет показано, как использовать именованные шаблоны совместно с тегами <xsl:import/> или <xsl:include/>, позволяющими добавлять внешние файлы и при необходимости загружать и выгружать шаблоны. Другой значимой функцией именованных шаблонов в XSLT является поддержка рекурсивного программирования.



В начало


Итерация

На примере входного документа catalog.xml рассмотрим способы повторения правила шаблона в наборе элементов. В данном случае узлом контекста будет корневой элемент или каталог. Вместо ранее представленного метода сопоставления элементов для принудительного запуска правил шаблонов можно использовать итерацию с тегом <xsl:for-each/>.

Во-первых, рассмотрим, как можно использовать тег xsl:for-each для просмотра в каталоге каждого элемента dvd:

<xsl:template match="catalog">
   <html>
      <body>
         <xsl:for-each select="dvd">
            <xsl:call-template name="DVD"/>
         <xsl:for-each>
      <body>
   </html>
</xsl:template>

Обратите внимание, что для представления в браузере используется HTML-форматирование. Посмотрев на содержимое тега xsl:for-each, можно заметить, что вызывается именованный шаблон DVD. Шаблон DVD может иметь следующую логику вывода:

<xsl:template name="DVD">
   <xsl:variable name="label" select="@code"/>
   <p>
      <img src="images/{$label}.gif" alt=""/>
      <xsl:value-of select="title"/>
   </p>
<xsl:template>

Переменные в XSLT

Стоит отметить, что xsl:variable является неизменной, то есть изменить ее значение в одной области невозможно. (Элемента xsl:constant нет, хотя такое имя было бы более подходящим).

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

Хотя переменные в XSLT не изменяются, можно объявлять переменную при каждом вызове в примере шаблона DVD, поскольку каждый раз создаются различные области.

В предыдущем примере показано несколько новых концепций. Во-первых, переменная xsl:variable с именем label. Обратите внимание, что эта переменная получает значение атрибута code элемента dvd, обозначенное префиксом "@". Также следует заметить, что xsl:for-each смещает по очереди узлы контекста в каждом элементе dvd. Это можно заметить в предыдущем шаблоне DVD, так как можно напрямую ссылаться на атрибут @code и значение элемента title, которые являются дочерними относительно элемента dvd. Эти относительные выражения XPath отражают тот факт, что xsl:for-each смещает узлы контекста для каждого из элементов dvd.

Во-вторых, новая переменная label используется в теге <img/> для быстрого нового HTML-форматирования. (Для каждого DVD предполагается наличие изображения в папке images/. Значение атрибута code DVD изображений соответствует имени файла изображения). Обратите внимание, что для получения значений переменной перед ней ставится знак доллара ("$"), затем переменная заключается в фигурные скобки ("{" и "}") подобно использованию элемента xsl:value-of. (Разумеется, в этом примере не требуется создавать переменную, вместо этого можно непосредственно указать на значение атрибута @code в коде img, например: <img src="images/{@code}.gif" alt=""/>.)

Все вместе должно выглядеть примерно так:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html"/>
   <xsl:template match="catalog">
      <html>
         <body>
            <xsl:for-each select="dvd">
               <xsl:call-template name="DVD"/>
            </xsl:for-each>
         </body>
      </html>
   </xsl:template>
   <xsl:template name="DVD">
      <xsl:variable name="label" select="@code"/>
      <p>
         <img src="images/{$label}.gif" alt=""/>
         <xsl:value-of select="title"/>
      </p>
   <xsl:template>
<xsl:stylesheet>

При выполнении предыдущего преобразования получаем следующее:

<html>
<body>
<p>
<img alt="" src="images/_1234567.gif">Terminator 2</p>
<p>
<img alt="" src="images/_7654321.gif">The Matrix</p>
<p>
<img alt="" src="images/_2255577.gif">Life as a House</p>
<p>
<img alt="" src="images/_7755522.gif">Raiders of the Lost Ark</p>
</body>
</html>



В начало


Форматирование выходных данных

Можно заметить, что в выводных данных больше нет в начале тега <?xml?>. Это обусловлено двумя причинами, любая из которых может привести к обработке выходных данных как HTML. Прежде всего добавлен тег <xsl:output method="html"/> сразу после тега открытия <xsl:stylesheet>. Атрибут method элемента xsl:output как правило имеет значение (html) или xml. Другим важным атрибутом тега xsl:output является атрибут encoding, определяющий набор символов выводимых данных и использующийся, если значение атрибута метода соответствует xml. Другая причина для исключения тега <?xml?> заключается в том, что большинство процессоров таблиц стилей определяют использование тегов HTML, например, <html/> и <body/>, включенных в сопоставленный шаблон. В этом случае метод автоматически рассматривается как HTML.



В начало


Сортировка результатов

Порядок документов
Порядок документов представляет собой очередность, в которой документы располагаются в XML-документе. Согласно этой схеме родительские элементы предшествуют дочерним; при этом корневой элемент становится первым элементом документа.

Полезным дополнением к xsl:for-each является xsl:sort. (Также используется элемент xsl:apply-templates). Этот тег также можно использовать в качестве первого дочернего тега xsl:for-each для изменения порядка результатов от порядка документов на другой порядок, основанный на некотором ключе. Например, тег xsl:for-each в предыдущем примере для сортировки DVD по значению атрибута title может использовать элемент xsl:sort:

<xsl:for-each select="dvd">
   <xsl:sort select="title"/>
   <xsl:call-template name="DVD"/>
</xsl:for-each>

Включив текст, предшествующий xsl:sort и выделенный полужирным шрифтом, в предыдущее преобразование, получим следующий результат:

<html>
<body>
<p>
<img alt="" src="images/_2255577.gif">Life as a House</p>
<p>
<img alt="" src="images/_7755522.gif">Raiders of the Lost Ark</p>
<p>
<img alt="" src="images/_1234567.gif">Terminator 2</p>
<p>
<img alt="" src="images/_7654321.gif">The Matrix</p>
</body>
</html>

Обратите внимание, что теперь DVD отсортированы по названиям в алфавитном порядке. После первого элемента можно добавлять дополнительные элементы xsl:sort для более точной сортировки результатов.



В начало


Рекурсия

Реализация предыдущего преобразования списка DVD с помощью рекурсии может показаться странной, поскольку:

  • В показанном итеративном решении использование xsl:for-each более понятно программистам, привыкшим работать с императивными языками, например, с языком программирования Java™;
  • Такая особая ситуация более подходит к методу использования for-loop, так как требуется всего лишь отслеживание набора сестринского узла.

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html"/>
   <xsl:template match="catalog">
      <html>
         <body>
            <xsl:call-template name="DVD">
               <xsl:with-param name="dvdCount" select="count(dvd)"/>
            </xsl:call-template>
         </body>
      </html>
   </xsl:template>
   <xsl:template name="DVD">
      <xsl:param name="position" select="1"/>
      <xsl:param name="dvdCount"/>
      <xsl:variable name="label" select="dvd[$position]/@code"/>
      <p>
         <img src="images/{$label}.gif" alt=""/>
         <xsl:value-of select="dvd[$position]/title"/>
      </p>
      <xsl:if test="$position < $dvdCount">
         <xsl:call-template name="DVD">
            <xsl:with-param name="position" select="$position+1"/>
            <xsl:with-param name="dvdCount" select="$dvdCount"/>
         </xsl:call-template>
      </xsl:if>
   <xsl:template>
</xsl:stylesheet>

Данное преобразование возвращает такой же результат, как и итеративное решение:

<html>
<body>
<p>
<img alt="" src="images/_2255577.gif">Life as a House</p>
<p>
<img alt="" src="images/_7755522.gif">Raiders of the Lost Ark</p>
<p>
<img alt="" src="images/_1234567.gif">Terminator 2</p>
<p>
<img alt="" src="images/_7654321.gif">The Matrix</p>
<body>
</html>

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

Рекурсия в сопоставленном шаблоне начинается в начале преобразования с вызова шаблона DVD. Затем DVD шаблон вызывает себя до тех пор, пока не станет последним рабочий элемент dvd.

В данном примере стоит также обратить внимание на следующее. Например, узел контекста никогда не смещается в элементы dvd. Это обусловлено тем, что элемент xsl:call-template в отличие от элемента xsl:for-each не изменяет узел содержимого. Благодаря этому переменная, определенная в шаблоне DVD, должна ссылаться на текущий элемент dvd с помощью параметра position элемента xsl:param, сохраняющего положение проверяемого элемента dvd. Параметры могут передавать значения из шабона в именованный шаблон с помощью тега <xsl:with-param/>. Тег <xsl:with-param> всегда является дочерним элементом тега <xsl:call-template/>. Параметры извлекаются из именованного шаблона с помощью тега <xsl:param/>, который всегда должен находиться сразу после открывающего тега <xsl:template/>.

Можно заметить, что значение по умолчанию параметра position элемента xsl:param в шаблоне DVD определяется атрибутом select. По этой причине в первом вызове шаблона DVD из сопоставленного шаблона не требуется использовать соответствующий элемент xsl:with-param. В теге xsl:param лучше использовать значения по умолчанию, это позволяет исключить неожиданное поведение в вызываемых шаблонах. (Если противопоставлять два параметра не требуется, значение параметра dvdCount также лучше оставить по умолчанию).

Обратите внимание, что в элементе xsl:call-template в конце шаблона DVD выполнено приращение параметра position при его передаче в следующий экземпляр шаблона DVD. В атрибуте test тега <xsl:if/> значения двух параметров используются в качестве основания условия остановки рекурсии.

Параметр position также используется в шаблоне DVD для указания проверяемого элемента dvd. Это выполняется с помощью предиката, используемого для уточнения элемента dvd в атрибуте select тегов xsl:variable и xsl:value-of. Оба этих тега являются выражениями XPath. Они будут описаны подробнее далее в данном руководстве. Предикаты XPath заключаются в квадратные скобки ("[" и "]") и следуют непосредственно за уточняемым элементом. Обратите внимание, что в таком случае параметр position используется в предикате для указания положения проверяемого элемента dvd: dvd[$position].

Элемент <xsl:if/> использовать довольно просто. В нем есть условие, проверяемое в обязательном атрибуте test на логический результат (true или false). Кстати, теги <xsl:else/> или <xsl:elseif/> отсутствуют. Вместо них используется тег <xsl:choose/>, который будет описан позднее. Наконец, следует заметить, что знак "меньше чем" в атрибуте test тега <xsl:if/> является XML-управляемым (&lt;>). При использовании неуправляемого символа "меньше чем" (<) процессор таблиц стилей обрабатывает исключение, поскольку этот символ, а также закрывающая угловая скобка или символ "больше чем" (>) используются для указания начала и конца XML-элементов.



В начало


Рекурсия для карты Web-сайта

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


Листинг 2. Экземпляр XML-документа для карты Web-сайта
                    
<?xml version="1.0"?>
<site>
   <page label="A" href="0.html">
      <page label="AA" href="0_0.html">
         <page label="AAA" href="0_0_0.html">
            <page label="AAAA" href="0_0_0_0.html"/>
            <page label="AAAB" href="0_0_0_1.html"/>
            <page label="AAAC" href="0_0_0_2.html"/>
         </page>
         <page label="AAB" href="0_0_1.html"/>
         <page label="AAC" href="0_0_2.html"/>
      </page>
      <page label="AB" href="0_1.html"/>
      <page label="AC" href="0_2.html"/>
   </page>
   <page label="B" href="1.html"/>
   <page label="C" href="2.html"/>
</site>

В примере карты сайта в листинге 2 имеется четыре уровня вложенности HTML-страниц по темам. Значения атрибута label трех страниц верхнего уровня - A, B и C. Страница A имеет три дочерние страницы (AA, AB и AC), первая дочерняя страница имеет три дочерних страницы (AAA, AAB и AAC). Наконец, страница AAA имеет несколько дочерних страниц (AAAA-AAAC). Данное дерево необходимо представить как HTML-документ.

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

Build List;

Build List {
   For each page {
      Write page;
      If (page has child pages)
         Build List;
   }
}

Алгоритм обрабатывает дерево навигации с любым количеством уровней, используя совсем немного кода (немного для XSLT). Краткость необходимого XSLT-кода показывает, насколько хорошо этот код подходит для решения данной задачи. Добавьте дефис синтаксиса HTML, выполняется преобразование с использованием предыдущей логики. Для привязки логики в алгоритм добавляются комментарии:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
         xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="site">
   <html>
      <body>
         <!-- Make the initial template call -->
         <xsl:call-template name="BuildList"/>
      </body>
   </html>
</xsl:template>
<xsl:template name="BuildList">
   <ul>
      <xsl:for-each select="page">
         <!-- Write the current page -->
         <li>
            <a href="{@href}">
               <xsl:value-of select="@label"/>
            </a>
         </li>
         <!-- If the page has child pages, -->
         <xsl:if test="count(page) > 0">
            <!-- Build that list of pages, starting the process again... -->
            <xsl:call-template name="BuildList"/>
         </xsl:if>
      <xsl:for-each>
   </ul>
</xsl:template>
</xsl:stylesheet>

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

<html>
<body>
   <ul>
      <li><a href="0.html">A</a></li>
      <ul>
         <li><a href="0_0.html">AA</a></li>
         <ul>
            <li><a href="0_0_0.html">AAA</a><li>
            <ul>
               <li><a href="0_0_0_0.html">AAAA</a><li>
               <li><a href="0_0_0_1.html">AAAB</a><li>
               <li><a href="0_0_0_2.html">AAAC</a></li>
            </ul>
            <li><a href="0_0_1.html">AAB<a></li>
            <li><a href="0_0_2.html">AAC</a><li>
         <ul>
         <li><a href="0_1.html">AB</a></li>
         <li><a href="0_2.html">AC</a><li>
      </ul>
      <li><a href="1.html">B<a></li>
      <li><a href="2.html">C</a><li>
   <ul>
</body>
</html>

С отключенными ссылками данный HTML-документ выводится следующим образом:



В начало


Рекурсия для модуля навигации по сайту

Предыдущее преобразование можно выполнить в модуле дерева навигации с добавлением логики для сравнения текущей страницы (узла контекста) со всеми элементами страниц на карте сайта. Это возможно благодаря тому, что для всех страниц на Web-сайте требуется своя собственная версия модуля навигации, основанная на положении страницы в дереве. Для управления открытием и закрытием сестринского набора страниц также потребуется JavaScript. После завершения вывода модуль выглядит аналогично стандартным графическим элементам в Microsoft Windows Explorer и в панели Eclipse Navigator view.

После начального вывода у HTML-элементов, представляющих все страницы за исключением страниц верхнего уровня свойству display задано значение "none" (более подробней об этом см. раздел CSS далее). Затем используйте в браузере логику JavaScript для развертывания HTML-тега, представляющего страницу-предка узла контекста с помощью задания свойству стиля display значения "block". Чтобы такое поведение было характерно для страниц, атрибут @href всех элементов страниц предполагается уникальным. Элемент xsl:variable можно использовать для хранения значения атрибута @href узла контекста. Затем, когда элементы страницы с помощью итерации на каждом уровне навигации получают контекст, можно сравнить атрибут @href элемента страницы с элементом xsl:variable, содержащим атрибут @href для выводимой страницы. Таким способом выводятся ссылки на текущую страницу и все страницы более высокого уровня.

Также можно показать дочерние страницы текущей страницы, если они существуют. Можно использовать HTML-элементы неупорядоченных списков, представленные ранее, или HTML-теги <div/> для хранения данных о всех уровнях навигации. Для этих элементов требуются уникальные значения атрибута id, поэтому можно программно переключать свойства стилей между display:none и display:block. При использовании тегов <div/> для обеспечения отступов текста необходимо использовать ненулевое значение стиля padding-left или margin-left. Примените данные рекомендации в качестве упражнения. Если добавить несколько изображений, результат преобразования можно сделать похожим на элемент навигации, представленный на рисунке 1. На рисунке показан HTML-вывод рекурсивного XSLT-преобразования, являющегося основой для большинства приводимых в данном руководстве рекомендаций. Вместо вложенных элементов можно использовать сестринские с отношением "указатель на родителя", реализованным с помощью пары атрибутов. HTML-вывод, представленный на рисунке 1, отображается в Internet Explore 6.0.


Рисунок 1. Модуль навигации
Модуль навигации

В выводимом HTML (а не в изображении на рисунке 1) значок "минус", показанный на рисунке 1, используется для развертывания и свертывания ветвей дерева, текст узла используется для перехода на страницу. (Все узлы на рисунке 1 развернуты для демонстрации всего дерева). После загрузки в браузере целевой страницы изначально все узлы свернуты, то есть отображаются только страницы верхнего уровня. Затем дерево можно развернуть, начиная с текущей страницы вверх (фактически, влево) до верхнего уровня, чтобы отображались все страницы-предки текущей страницы и сама страница. Открытие дерева на текущей странице также приводит к отрытию ее дочерних страниц.

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

Вложенная структура XML-документов и неизменность элементов xsl:variable позволяют решать и кодировать большинство задач в XSLT с использованием рекурсии. Как правило, фокус заключается в разработке шаблонов, например, шаблона BuildList, достаточно общих для решения любых возникающих проблем. При наличии таких шаблонов их можно просто вызывать при необходимости.



В начало


Условная логика

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

Изменим примененное ранее итеративное решение для отображения всех элементов dvd с помощью вызова шаблона Price, написанного ранее:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="html"/>
   <xsl:template match="catalog">
      <html>
         <body>
            <xsl:for-each select="dvd">
               <xsl:call-template name="DVD"/>
            </xsl:for-each>
         </body>
      </html>
   </xsl:template>
   <xsl:template name="DVD">
      <p>
      <xsl:value-of select="title"/>
         <xsl:call-template name="Price"/>
      </p>
   </xsl:template>
   <xsl:template name="Price">$<xsl:value-of select="price"/><xsl:template>
</xsl:stylesheet>

Теперь изменить шаблон Price для отображения вместо суммы в долларах одной из следующих характеристик ("Pricey!", "Cheap!" и "So so"):

<xsl:template name="Price">
   <xsl:choose>
      <xsl:when test="price < 15.00"> - Cheap!</xsl:when>
      <xsl:when test="price > 19.00"> - Pricey!</xsl:when>
      <xsl:otherwise> - So so</xsl:otherwise>
   </xsl:choose>
</xsl:template>

Обратите внимание, что у элемента xsl:choose имеется два дочерних элемента: обязательный элемент xsl:when, аналогичный xsl:if, и дополнительный элемент xsl:otherwise. Вывод данного преобразования с использованием элемента xsl:choose выглядит следующим образом:

<html>
<body>
<p>Terminator 2 - Pricey!</p>
<p>The Matrix - Cheap!</p>
<p>Life as a House - So so<p>
<p>Raiders of the Lost Ark - Cheap!</p>
</body>
</html>

Как и большинство XSLT-кодировщиков, вы вряд ли будете поражены, поскольку здесь нет элементов xsl:else и xsl:else-if, а элемент xsl:choose слишком подробный. Для этого в XSLT 2.0 имеется решение, но в данном руководстве оно обсуждаться не будет. В книге XML in a Nutshell, 3rd Edition (см. раздел Ресурсы) в большинстве глав имеются разделы с рассмотрением различий версий XSLT 1.0 и 2.0 и XPath.



В начало


Импорт и использование шаблонов из других файлов

Два элемента xsl:import и xsl:include позволяют добавлять к преобразованию шаблоны из других файлов. Эти элементы являются дочерними элемента <xsl:stylesheet/> или <xsl:transform/> и должны размещаться до других основных элементов. Синтаксис этих элементов следующий:

<xsl:import href="URI"/>

and

<xsl:include href="URI"/>

Значение URI для атрибутов href ссылается на путь и имя файла, содержащего добавляемое преобразование. Значение URI может быть относительным или абсолютным. Эти элементы различаются тем, что шаблоны, встроенные с помощью элемента xsl:import, могут иметь конфликты имен, в этом случае такие шаблоны игнорируются. При использовании xsl:include при импорте преобразования именованные шаблоны, принадлежащие добавляемым преобразованиям, не могут конфликтовать с другими шаблонами. Эти файлы просто копируются в текущее преобразование в точке xsl:include. Для обоих элементов нельзя использовать циклические ссылки между импортированными и импортируемыми файлами. Как и для других свойств XML, с помощью элементов xsl:import и xsl:include можно использовать вложенность файлов.



В начало


XSLT: Заключение

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



В начало



На предыдущую страницуСтраница 3 из 9 На предыдущую страницу
    IBM в России Конфиденциальность Контакты