Xslt файл. Преобразование XML_документа с помощью XSLT. XSLT-преобразования на web-серверах

22. DOM XML. Преобразование XML документов

Модель XML DOM. SAX: достоинства и недостатки. Спецификация XSL. XSLT и XPath. XSL-FO. XQuery.

Для программной обработки XML документов используется модель XML DOM, которая определяет объекты и свойства всех XML элементов и методы (интерфейс) для доступа к ним. Иначе говоря, XML DOM описывает каким образом необходимо получать, изменять, добавлять и удалять XML элементы.

Согласно DOM:

    все, что содержится внутри XML документа, является узлом;

    весь документ представляется узлом документа ;

    каждый XML элемент – узел элемента ;

    текст внутри XML элементов - текстовый узел ;

    каждый атрибут - узел атрибута ;

    комментарии - узлы комментариев .

XML документ в соответствии с моделью XML DOM представляется как дерево из узлов, при этом:

    Все узлы дерева находятся в определенных отношениях друг с другом.

    Все узлы доступны через дерево. Их содержимое может быть изменено, удалено; новые элементы могут быть добавлены в дерево.

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

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

    Эти отношения описываются с помощью понятий родитель, дочерний и потомок (все дочерние на одном уровне).

Альтернативным интерфейсом для обработки XML документов является SAX .

SAX (Simple API for XML ) - прикладной программный интерфейс для парсера с последовательным доступом к XML. Этот интерфейс предоставляет механизм чтения данных из XML документа.

SAX парсер является поточным и управляемым событиями . Задачей пользователя SAX API заключается в описании методов, вызываемых событиями, возникающими при анализе документа.

Такими событиями могут быть следующие:

    текстовый узел;

    узел элемента XML;

    инструкция обработки XML;

    комментарий XML.

События вызываются появлением как открывающего тэга, так и закрывающего тэга любого из этих элементов документа. Атрибут XML также рассматривается как событие.

Анализ документа является однонаправленным (т.е. без возвратов по дереву).

В отличие от DOM формальной спецификации для SAX не существует. В качестве нормативной рассматривается Java реализация SAX.

Следует отметить следующие достоинства и недостатки SAX.

Достоинства :

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

    Скорость работы выше за счет сокращения затрат времени на выделение памяти для элементов дерева в случае DOM.

    Потоковое чтение данных с диска в случае DOM невозможно. Если для размещения всего документа в памяти недостаточно места, то использование SAX является безальтернативным.

Недостатки :

    Процедура проверки правильности предполагает доступ ко всему документу одновременно.

    Это также требуется и в случае XSLT преобразования.

Если загрузить "чистый" XML документ в веб-браузер, то можно будет увидеть древовидную структуру этого документа:

В этом как раз и заключается главное отличие между XML и HTML, а именно разделение структуры документа и его представления в браузере. Конкретный вид XML документа описывается отдельно с помощью CSS или XSL.

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

С помощью XSL можно преобразовать XML-документ в формат HTML , WML , RTF , PDF , SQL , SWF , а так же в другой XML и XSL документ. XSL указывает как будет оформлен документ, где и каким образом должны размещаться данные.

Cпецификация XSL состоит из трех частей:

    XSLT (XSL Transformations), язык для преобразования XML;

    XPath - язык путей и выражений, используемый в XSLT для доступа к отдельным частям XML-документа;

    XSL-FO (XSL Formatting Objects), язык для верстки XML.

Наиболее распространенным механизмом XSLT преобразований для систем работающих на платформе Microsoft Windows является MSXML ; для систем на основе GNU - xsltproc .

Для того, чтобы обработать XML документ c помощью XSL, необходимо в XML документе написать следующую инструкцию:

< !-- ... -- >

Возвращаясь к рассмотренному ранее примеру, добавив в XML файл ссылку на XSL файл, получим следующий код разметки:

Проверка почтового ящика

[email protected]

[email protected]

[email protected]

Test

Это письмо не является спамом

[email protected]

[email protected]

Интересное предложение

После загрузки данного документа в веб-браузере его вид кардинально изменится:

Содержимое XSL файла mailbox.xsl приводится ниже:

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

Почтовый ящик

From: To:

,

Subject:CCNotify




XSLT и XPath

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

Благодаря XSLT реализуется отделение данных от их представления в рамках парадигмы MVC (Model-view-controller ).

XPath (XML Path Language) - язык запросов к элементам XML-документа. XPath был разработан для организации доступа к частям документа XML в файлах трансформации XSLT и является стандартом консорциума W3C. В языке XPath используется компактный синтаксис, отличный от принятого в XML. Начиная с версии 2.0, XPath является составной частью языка XQuery . XPath призван помочь обходить всевозможные деревья, получать необходимые элементы из другой ветви относительно точки обхода, распознавать предков, потомков, атрибуты элементов. Это полноценный язык навигации по дереву.

Для нахождения элемента(ов) в дереве документа используются пути адресации.

Каждый шаг адресации состоит из трех частей:

    оси, например child:: ;

    условия проверки узлов, например имена элементов документа body, html;

    предиката, например attribute::class .

Дополнением к ядру языка является набор функций, которые делятся на 5 групп: системные функции, функции с множествами , строковые функции, логические функции, числовые функции.

XSL-FO

XSL-FO (eXtensible Markup Language Formatting Objects) - рекомендованный W3C язык разметки предпечатных материалов. По-сути, XSL-FO - это унифицированный язык представления. Он не имеет семантической разметки (как в HTML) и сохраняет все данные документа внутри себя (в отличие от CSS, который модифицирует представление по умолчанию для внешнего HTML или XML-документа) .

В результате применения XSLT-преобразования к исходному XML документу получается его описание на языке XSL-FO . FO-процессор конвертирует XSL-FO-документ в какой-либо читаемый и/или печатаемый формат. Наиболее часто используется преобразование в PDF либо PS; некоторые FO-процессоры могут давать на выходе RTF-файлы либо просто показывать документ в окне.

XQuery

XQuery - язык запросов, разработанный для обработки данных в формате XML.

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

В рамках стандарта SQL:2006 разработаны механизмы для встраивания XQuery -запросов непосредственно в SQL-запросы.

Преобразования из XML в XML

Преобразования XML-XML иногда рассматриваются как SQL для Интернета, поскольку они позволяют оперировать запросами к базе данных в XML-документах. Ниже приведен пример. Используемый нами файл planets.xml содержит достаточно много данных о каждой планете:

Mercury

.0553

58.65

1516

.983

43.4

Venus

.815

116.75

3716

.943

66.8

Что, если нам нужно только подмножество этих данных - например, имя и масса каждой планеты? В терминах баз данных planets.xml представляет собой таблицу, и мы хотим создать новую таблицу, содержащую подмножество данных из первой. В базах данных для этого служит язык SQL, а для документов XML мы можем использовать XSLT.

В листинге 1.6 приведена новая версия файла planets.xsl , осуществляющая требуемое преобразование: выбираются только имя и масса каждой планеты, которые отправляются в выходной документ. В особенности обратите внимание на то, что мы осуществляем преобразование XML-XML, поэтому я использую элемент , атрибут method которого установлен в «xml» (фактически тип выходных данных обычно и есть XML, но если процессор XSLT видит тег , он обычно по умолчанию генерирует HTML).

Листинг 1.6. Выбор только имени и массы

xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

C:planets>java org.apache.xalan.xslt.Process -IN planets.xml -XSL planets.xsl -OUT new.xml

Вот как выглядит результирующий документ XML, new.xml:

Mercury

.0553(Earth = 1)

Venus

.815(Earth = 1)

Earth

1(Earth = 1)

Отметьте, что этот файл выглядит во многом похоже на исходный файл planets.xml , за тем исключением, что каждый элемент Содержит только элементы и . Таким образом, мы смогли получить подмножество данных первоначального документа XML.

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

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

Из книги C++ автора Хилл Мюррей

8.5.6 Преобразования Конструктор, получающий один параметр, определяет преоразование из типа своего параметра в тип своего класса. Такие преобразования неявно применяются дополнительно к стандартным пробразованиям (#6.6-7). Поэтому присваивание объекту из класса X

Из книги Справочное руководство по C++ автора Страустрап Бьярн

R.4.7 Преобразования ссылок Всюду, где ссылки (§R.8.2.2) инициализируются (включая передачу параметров (§R.5.2.2) и возврат значения функции (§R.6.6.3)) или используются иным образом, возможны следующие преобразования:Ссылка на данный класс может быть преобразована в ссылку на

Из книги Советы по Delphi. Версия 1.0.6 автора Озеров Валентин

R.12.3 Преобразования Преобразования объектов класса можно задать с помощью конструкторов или функций преобразования.Такие преобразования, обычно называемые пользовательскими, используются неявно в совокупности со стандартными преобразованиями (§R.4). Например, функцию

автора Реймонд Эрик Стивен

R.12.3.2 Функции преобразования Функция-член класса X, имя которой имеет вид,имя-функции-преобразования: operator имя-типа-преобразованияимя-типа-преобразования: список-спецификаций-типа opt операция-ptr optзадает преобразование из типа X в тип, определяемый конструкцией

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Из книги HTML 5, CSS 3 и Web 2.0. Разработка современных Web-сайтов. автора Дронов Владимир

Из книги XSLT автора Хольцнер Стивен

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

Из книги Технология XSLT автора Валиков Алексей Николаевич

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

Из книги Язык программирования Си для персонального компьютера автора Бочков C. О.

Преобразования из XML в XML Преобразования XML-XML иногда рассматриваются как SQL для Интернета, поскольку они позволяют оперировать запросами к базе данных в XML-документах. Ниже приведен пример. Используемый нами файл planets.xml содержит достаточно много данных о каждой

Из книги Macromedia Flash Professional 8. Графика и анимация автора Дронов В. А.

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

Из книги автора

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

Из книги автора

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

Из книги автора

Упрощенные преобразования Многие простые преобразования состоят из единственного правила, которое обрабатывает корневой узел входящего документа. Общий вид такого рода преобразований показан в следующем листинге.Листинг 4.2. Простое

Из книги автора

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

Из книги автора

Преобразования типов Преобразование типов производится либо неявно, например при преобразовании по умолчанию или в процессе присваивания, либо явно, путем выполнения операции приведения типа. Преобразование типов выполняется также, когда преобразуется значение,

Из книги автора

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

Итак, дерево объектов D0M построено надлежащим образом. Теперь надо его преобразовать в документ XML, страничку HTML, документ PDF или объект другого типа. Средства для выполнения такого преобразования составляют третью часть набора JAXP - пакеты javax. xml. transform, javax.xml.transform.dom, javax.xml.transform.sax, javax.xml.transform.stream, которые представляют собой реализацию языка описания таблиц стилей для преобразований XSLT (XML Stylesheet Language for Transformations) средствами Java.

Язык XSLT разработан консорциумом W3 как одна из трех частей, составляющих язык записи таблиц стилей XSL (XML Stylesheet Language). Все материалы по XSL можно посмотреть на сайте проекта http://www.w3.org/Style/XSL/ .

Интерфейсы и классы, входящие в пакеты javax. xml. trans form. *, управляют процессором XSLT, в качестве которого выбран процессор Xalan, разработанный в рамках проекта Apache Software Foundation, http://xml.apache.org/xalan-j/ .

Исходный объект преобразования должен иметь Интерфейс

Source определяет всего два метода доступа к идентификатору объекта:

public String getSystemId() ; public void setSystemId(String id);

У интерфейса source есть три реализации. Класс DOMSource подготавливает к преобразованию дерево объектов D0M, а классы SAXsource и streamSource подготавливают SAX-объект и простой поток данных. В конструкторы этих классов заносится ссылка на исходный объект - для конструктора класса DOMSource это узел дерева, для конструктора класса SAXSource - ИМЯ файла, ДЛЯ КОНСТруКТОра Класса StreamSource - ВХОДНОЙ поток. Методы этих классов позволяют задать дополнительные свойства исходных объектов преобразования.

Результат преобразования описывается интерфейсом Result. Он тоже определяет точно такие же методы доступа к идентификатору объекта-результата, как и интерфейс У тоже есть три реализации - классы

DOMResult, SAXResult И StreamResult. В КОНСТруКТОрЫ ЭТИХ классов ЗЭНОСИТ- ся ссылка на выходной объект. В первом случае это узел дерева, во втором - объект типа ContentHandler, в третьем - файл, в который будет занесен результат преобразования, или выходной поток.

Само преобразование выполняется объектом класса Transformer. Вот стандартная схема преобразования дерева объектов в документ записываемый в файл.

TransformerFactory transFactory =

TransformerFactory.newInstance();

Transformer transformer = transFactory. newTrans former () ;

DOMSource source = new DOMSource (document) ;

File newXMLFile = new File ("ntbl.xml") ;

FileOutputStream fos = new FileOutputStream(newXMLFile) ;

StreamResult result = new StreamResult(fos); transformer.transform(source, result);

Вначале методом newlnstance о создается экземпляр transFactory фабрики объектов-преобразователей. Методом

public void setAttrbute(string name, String value);

класса Trans formerFactory можно установить некоторые атрибуты экземпляра. Имена и значения атрибутов зависят от реализации фабрики.

С помощью фабрики преобразователей создается объект-преобразователь класса Transformer. При создании этого объекта в него можно занести объект, содержащий правила преобразования, например, таблицу стилей XSL.

В созданный объект класса Transformer методом public void setParameter (String name, String value) ; можно занести параметры преобразования, а методами public void setOutputProperties(Properties out); public void setOutputProperty(String name, String value);

можно определить свойства преобразованного объекта. Имена свойств name задаются константами, которые собраны в специально определенный класс outputKeys, содержащий только эти константы. Вот их список:

Cdata_section_elements - список имен секций cdata через пробел.

Doctype _public - открытый идентификатор public преобразованного документа.

Doctype_system - системный идентификатор system преобразованного документа.

ENCODING - кодировка символов преобразованного документа, значение атрибута encoding объявления XML.

INDENT - делать ли отступы в тексте преобразованного документа. Значения этого свойства "yes" или "по".

Mediatype - МШЕ-тип содержимого преобразованного документа.

METHOD - метод вывода, одно из значений "xml", "html" или "text".

Omrr_xml_declaration - не включать объявление XML. Значения "yes" или "по".

Standalone - отдельный или вложенный документ, значение атрибута standalone объявления XML. Значения "yes" или "по".

Version - номер версии XML для атрибута version объявления XML.

Например, можно задать кодировку символов преобразованного документа следующим методом:

transformer.setOutputProperty(OutputKeys.ENCODING, "Windows-1251");

Затем в приведенном примере по дереву объектов document типа Node создается объект класса DOMSouroe - упаковка дерева объектов для последующего преобразования. По типу аргумента конструктора видно, что можно преобразовать не все дерево, а какое-либо его поддерево, записав в конструкторе класса DOMSource корневой узел поддерева.

Преобразование формата XML-данных посредством преобразований расширяемого языка таблиц стилей (XSLT)

Перед началом работы

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

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

О чем это руководство?

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

В данном руководстве вы изучите:

  • Основы XSLT
  • Использование простых шаблонов
  • Публикацию данных
  • Управление пробелами
  • Основы XPath
  • Функции XPath
  • Циклы и условные операторы
  • Импорт и включение других таблиц стилей
  • Расширение XSLT
  • Переменные XSLT

Начало работы

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

Например, у вас могут быть данные в XML, которые вы хотите опубликовать в Интернете, что означает перевод их в HTML. Конечно, вы могли бы вручную просмотреть документ и сделать в нем необходимые изменения, или может быть, вы подумали о том, чтобы загрузить XML в DOM, а затем вручную создать документ вывода.

Что такое XSLT?

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

Давайте посмотрим пример.

Поставленная задача

В данном руководстве мы возьмем XML-документ и преобразуем его в XHTML-документ, который можно отобразить как Web-страницу. Входные данные - это просто файл с рецептами (см. листинг 1).

Листинг 1. Основные данные
Gush"gosh 1pound hamburger 1pound elbow macaroni 2cups brown sugar 1bag chopped onions 1teaspoon dried dill Brown the hamburger. Add onions and cook until transparent. Add brown sugar and dill. Cook and drain pasta. Combine meat and pasta. A balanced breakfast 1cup cereal 1glass orange juice 1cup milk 2slices toast Combine cereal and milk in bowl. Add all ingredients to table.

Замечание редактора : Эти рецепты –приведены просто для примера, так, как их представляет себе автор. Правильный рецепт для Gush"gosh (полученный от его жены, которая и готовит это блюдо) состоит из 1 фунта (0,454 кг) рубленой говядины, 1 фунта рожков, 1/2 стакана желтого сахара, 1 небольшого пакета (около 300 грамм) мелко нарезанного лука, 1 чайной ложки сушеного укропа и 1 небольшой банки томатной пасты, в которую добавляется желтый сахар.

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

Наша цель - преобразовать эти данные в XHTML-страницу, которая будет отображать рецепты по отдельности и форматировать их ингредиенты и инструкции по приготовлению (см. листинг 2).

Листинг 2. Результат
Recipe

Gush"gosh

Ingredients:

1 pound hamburger
1 pound elbow macaroni
2 cups brown sugar
1 bag chopped onions
1 teaspoon dried dill

Directions:

  1. Brown the hamburger.
  2. Add brown sugar and dill.
  3. Cook and drain pasta.
  4. Combine meat and pasta.

A balanced breakfast

Ingredients:

1 cup cereal
1 glass orange juice
1 cup milk
2 slices toast

Directions:

  1. Add all ingredients to table.

Вы можете отобразить этот результат в браузере, как показано на рисунке 1.

Рисунок 1. Результат, отображенный в браузере

Как уже упоминалось, конечной целью может быть любой формат, не только XHTML, и даже не обязательно XML.

Давайте начнем с простых преобразований.

Простая таблица стилей

Самая простая таблица стилей - это просто XML документ, включающий XSLT-вывод (см. листинг 3).

Листинг 3. Самая простая таблица стилей
Recipe

Ingredients:

Directions:

Обратите внимание на использование пространства имен xsl . Добавление этого пространства имен говорит процессору, какие элементы связаны с обработкой, а какие должны быть просто выведены. Элементы value-of говорят процессору вставить определенные данные в это место. Какие именно данные вставлять определяется содержимым атрибута select .

Атрибут select состоит из выражения XPath. Подробнее XPath будет обсуждаться в разделе , однако здесь вы можете видеть, что доступ к элементам "название", "ингредиенты" и "инструкция по приготовлению" происходит через иерархию документа. Мы начинаем с корневого элемента, /recipes , и от него движемся вниз.

Как выполнить преобразование

Простейший способ выполнить XML-преобразование - это добавить указание на таблицу стилей в XML и отобразить его в браузере (см. листинг 4).

Листинг 4. Добавление в XML инструкции по обработке при помощи таблицы стилей
Gush"gosh ...

Эта инструкция по обработке говорит браузеру извлечь таблицу стилей, расположенную в basicstylesheet.xsl, и использовать ее для преобразования XML-данных и вывода результатов. Если вы откроете наш XML-документ в браузере Microsoft® Internet Explorer®, то увидите результат, похожий на рисунок 2.

Рисунок 2. Извлечение таблицы стилей и преобразование XML-данных

Однако это не совсем то, что мы хотели получить. Если вы выберете в браузере Вид>Просмотр HTML-кода, то увидите изначальный XML. Чтобы увидеть результат преобразования, необходимо произвести это преобразование и создать выходной файл. Это можно сделать через командную строку, используя Java-код со следующей командой (см. листинг 5):

Листинг 5. Преобразование документа через командную строку
java org.apache.xalan.xslt.Process -IN recipes.xml -XSL basicstylesheet.xsl -out result.html

Если вы получите исключение ClassNotFoundException , возможно, вам нужно загрузить Apache Xalan (см. "Получить продукты и технологии" в разделе ) и добавить включенные в него JAR-файлы в путь к классам.

Выполнив преобразование, показанное в листинге 5, вы увидите, что файл result.html содержит следующий код (см. листинг 6).

Листинг 6. Результаты
Recipe

Gush"gosh

Ingredients:

1poundhamburger 1poundelbow macaroni 2cupsbrown sugar 1bagchopped onions 1teaspoondried dill

Directions:

Brown the hamburger. Add onions and cook until transparent. Add brown sugar and dill. Cook and drain pasta. Combine meat and pasta.

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

Добавление шаблонов

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

Создание шаблонов

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

Листинг 7. Переделанная таблица стилей
Recipe

Ingredients:

Directions:

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

Но это не то, что мы хотим. Мы хотим иметь возможность форматировать ингредиенты и инструкции. Для этого мы можем создать отдельные шаблоны для каждого из этих элементов и включить их в таблицу стилей (см. листинг 8).

Листинг 8. Создание дополнительных шаблонов
Recipe

Ingredients:

Directions:

INGREDIENTS HERE

INSTRUCTIONS HERE

Обратите внимание, что вместо того, чтобы просто вывести элемент value-of , мы теперь указываем таблице стилей применять соответствующие шаблоны к элементам ingredients и instructions. Затем мы создали отдельные шаблоны для этих элементов, задав их в атрибуте match . Добравшись до элемента apply-templates , процессор выбирает все элементы ingredients в документе. Затем он ищет шаблон для ингредиентов, а, найдя его, выводит этот шаблон. То же самое он делает с элементом instructions . Результат должен быть похож на изображенный на рисунке 3.

Рисунок 3. Применение соответствующих шаблонов к элементам ingredients и instructions

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

Публикация шаблонов

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

Листинг 9. Выделение рецептов
Recipe

Ingredients:

Directions:

INGREDIENTS HERE

INSTRUCTIONS HERE

В данном случае таблица стилей выводит основную страницу (HTML), а затем просматривает каждый рецепт, выводя название, ингредиенты и инструкции для каждого рецепта. Опять-таки XPath мы будем изучать в разделе , но в данном случае элемент recipe становится контекстным узлом, поэтому атрибуты select относятся к этому узлу так же как файлы к конкретному каталогу в файловой системе. Результат должен быть похож на рисунок 4.

Рисунок 4. Элемент recipe становится контекстным узлом

Отлично, формат приблизился к желаемому, однако нам все еще нужно отобразить фактическую информацию. Для этого надо изменить шаблоны ingredients и instructions (см. листинг 10).

Листинг 10. Работа над шаблонами ingredients и instructions
...

Ingredients:

Directions:


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

    Наоборот, мы воспользовались тем, что когда указали процессору применять подходящие шаблоны к элементу ingredients , он ищет не только элемент ingredients , но и дочерние элементы элемента ingredients . Таким образом, он находит шаблон для ингредиентов, в котором вы выводите количество, единицы измерения, название продукта и разрыв строки.

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

    Результат должен быть похож на рисунок 5.

    Рисунок 5. Создание упорядоченного списка в основном шаблоне рецепта

    Определенно формат приближается к желаемому. Однако если вы посмотрите на вывод, то увидите, что проблема с пробелами все еще существует (см. листинг 11).

    Листинг 11. Недоработанный вывод
    Recipe

    Gush"gosh

    Ingredients:

    1poundhamburger
    1poundelbow macaroni
    2cupsbrown sugar
    1bagchopped onions
    1teaspoondried dill

    Directions:

    1. Brown the hamburger.
    2. Add onions and cook until transparent.
    3. ...

      Добавление пробелов

      Почему это случилось, если мы включали пробелы в таблицу стилей? Должны они были появиться в выводе? Разумеется, необязательно. Есть способы указать таблице стилей оставлять пробелы - они будут изучены в разделе Организация циклов и импорт - однако в некоторых случаях проще явно добавить текст в вывод (см. листинг 12).

      Листинг 12. Добавление текста
      ...
      ...

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

      Листинг 13. Окончательный вывод
      Recipe

      Gush"gosh

      Ingredients:

      1 pound hamburger
      1 pound elbow macaroni
      2 cups brown sugar
      1 bag chopped onions
      1 teaspoon dried dill

      Directions:

      1. Brown the hamburger.
      2. Add onions and cook until transparent.
      3. Add brown sugar and dill.
      4. Cook and drain pasta.
      5. Combine meat and pasta.

      A balanced breakfast

      Ingredients:

      1 cup cereal
      1 glass orange juice
      1 cup milk
      2 slices toast

      Directions:

      1. Combine cereal and milk in bowl.
      2. Add all ingredients to table.

      Результат показан на рисунке 6.

      Рисунок 6. Проблема отсутствующих пробелов решена

      Основы XPath

      Возможность преобразовывать данные в форму, в какой вы желаете их видеть, требует понимания языка маршрутов XML, или XPath, который позволяет контролировать, какие именно данные публикуются и/или отображаются. Данный раздел объясняет основные понятия XPath и показывает, как создавать простые выражения.

      Что такое XPath?

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

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

      Вы также узнаете о некоторых более мощных функциях XPath и о предикатах - это в основном условные утверждения, которые можно добавлять в выражения. Например, в разделе Настройка контекста вы увидите, как выбирать все элементы recipe в документе; предикаты позволяют выбрать только конкретный элемент на основании конкретных критериев.

      Наконец, вы узнаете о еще более мощных функциях, которые позволяют использовать многие типы логики, которые используются в процедурных программах.

      Давайте начнем с изучения контекста выражения.

      Настройка контекста

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

      Листинг 14. Демонстрация контекста

      Здесь есть новый элемент - copy-of . Там, где value-of выводит содержимое элемента, copy-of делает именно то, о чем говорит его название, и выводит фактический узел, на который ссылается атрибут select .

      В данном случае в атрибуте select содержится одно из простейших возможных выражений XPath. Одиночная точка (.) – это ссылка на данный контекстный узел, как в файловой системе, когда вы хотите сослаться на "текущую директорию, вне зависимости от того, что это за директория." Поэтому если вы выполните это преобразование, то получите результат, как в листинге 15.

      Листинг 15. Простейшее преобразование
      Gush"gosh 1pound hamburger 1pound elbow macaroni 2cups brown sugar 1bag chopped onions 1teaspoon dried dill Brown the hamburger. Add onions and cook until transparent. Add brown sugar and dill. Cook and drain pasta. Combine meat and pasta. ...

      Вы видите, что это просто повторение того же документа; это потому, что контекстный узел, как указано атрибутом шаблона match, - это корень документа, или /. Если вы измените контекстный узел, вывод изменится. Например, вы можете установить контекстный узел на первый рецепт (см. листинг 16).

      Листинг 16. Перемещение контекстного узла

      Теперь, запустив преобразование, вы увидите разницу (см. листинг 17).

      Листинг 17. Результаты
      Gush"gosh 1pound hamburger 1pound elbow macaroni 2cups brown sugar 1bag chopped onions 1teaspoon dried dill Brown the hamburger. Add onions and cook until transparent. Add brown sugar and dill. Cook and drain pasta. Combine meat and pasta. A balanced breakfast 1cup cereal 1glass orange juice 1cup milk 2slices toast Combine cereal and milk in bowl. Add all ingredients to table.

      Так как мы переместили контекстный узел, вывод изменился. Это важно, когда вы хотите выбрать узел относительно контекстного узла. Например, вы можете выбрать только заголовок рецепта (см. листинг 18).

      Листинг 18. Выбор только заголовка

      Здесь вы выбираете элемент name , расположенный на один уровень ниже, чем текущий контекстный узел, поэтому результат будет как в листинге 19.

      Листинг 19. Результаты
      Gush"gosh A balanced breakfast

      Мы изучим задание узлов по номеру позже, а пока отметим, что мы можем переместить контекстный узел во второй рецепт (см. листинг 20).

      Листинг 20. Еще одно перемещение контекстного узла

      (Не обращайте внимания на еще один шаблон, он нужен, чтобы не появлялся текст другого рецепта.)

      Мы можем видеть, что результирующее преобразование показывает это изменение контекста (см. листинг 21).

      Листинг 21. Результаты
      A balanced breakfast

      Для чего нужны оси

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

      Для начала давайте поговорим о системе обозначений. Мы использовали упрощенную, укороченную форму запроса потомков. В "длинной форме" выражения задаются как child::name вместо./name

      В обоих случаях вы задаете любой узел, который является потомком контекстного узла, а также является элементом name . Вы также можете связать их вместе, как в предыдущем случае, используя /child::recipes/child::recipe вместо /recipes/recipe .

      Потомки

      Возможно, вы недоумеваете, зачем мучаться с длинной формой, если короткая настолько проще. Это не было бы нужно, если бы единственное, что вы могли бы делать - это выбор потомков. Однако эту нотацию также можно использовать для выбора других отношений. Например, выражение XPath descendant::instruction выбирает все элементы instruction, которые являются потомками контекстного узла, а не только дочерние элементы. Кроме того, можно комбинировать инструкции. Например, вы можете выбрать все инструкции второго рецепта: /recipes/recipe/descendant::instruction .

      Одной из разновидностей оси потомков является ось потомок-или-сам-элемент, которая выбирает заданный узел из всех потомков, а также ищет в самом контекстном узле. Например, выражение descendant-or-self::instructions выбирает все узлы instruction в контекстном узле и ниже его. Обычным сокращением для этой оси является двойная косая черта, //. Это означает, что выражения: /recipes/recipe//instructions и //instructions выбирают все инструкции для второго рецепта и все инструкции в документе, соответственно. Этот второй пример очень широко применяется, он удобен, когда вы хотите просто выбрать все элементы определенного типа в документе.

      Атрибуты

      Еще одна общая задача, с которой вы столкнетесь - это необходимость выбрать атрибут для конкретного элемента. Например, вы, возможно, захотите выбрать recipeId для конкретного рецепта. Выражение /recipes/recipe/attribute::recipeId выбирает атрибут recipeId для всех элементов recipe . Эта ось также имеет сокращенную форму: это же выражение можно записать так: /recipes/recipe/@recipeId .

      Родители

      Все, что мы видели до этого, переносило нас вниз по дереву иерархии, но также существует возможность пойти вверх , выбрав предка конкретного узла. Предположим, что контекстным узлом была одна из инструкций, а вы хотите вывести recipeId для текущего рецепта. Вы можете сделать это следующим образом: ./parent::node()/parent::node()/@recipeId .

      Это выражение начинается в текущем узле - instruction -, а затем двигается к предку этого узла - элементу instructions - а затем к предку ТОГО элемента - recipe -, а затем к соответствующему атрибуту. Возможно, вам лучше знакома сокращенная форма: ./../../@recipeId .

      Теперь давайте посмотрим, как задаются некоторые условия.

      Более продвинутые возможности XPath

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

      Использование предикатов

      Зачастую требуется не просто любой узел, а конкретный узел, выбранный на основе конкретных условий. Вы ранее уже видели пример, когда использовали выражение /recipes/recipe//instructions . Это на самом деле сокращенная версия выражения /recipes/recipe//instructions , которое означает, что процессор XPath должен пройти через каждый элемент recipes (конечно, такой элемент только один) и для каждого элемента recipes пройти через каждый элемент recipe. Для каждого элемента recipe проверить истинность выражения position() = 2 . (Другими словами, есть ли в списке этот второй рецепт?) Если это предложение, называемое предикатом, истинно, то процессор использует этот узел и продолжает, возвращая любые инструкции.

      С помощью предикатов можно сделать множество вещей. Например, можно вернуть только рецепты, в которых есть название: /recipes/recipe . Это выражение просто проверяет, существует ли элемент name - потомок элемента recipe . Также можно поискать конкретные значения. Например, можно возвращать только элементы, которые называются "A balanced breakfast": //recipe .

      Имейте в виду, что предикат просто говорит процессору, возвращать ли текущий узел, поэтому в данном случае возвращается элемент recipe, а не имя. С другой стороны, можно указать процессору возвратить только название первого рецепта при помощи любого и этих двух выражений (см. листинг 22).

      Листинг 22. Возвращение только названия первого рецепта
      //recipe[@recipeId="1"]/name //name]

      В первом выражении сначала мы выбираем все элементы recipe , а затем возвращаем только тот, у которого есть атрибут элемента recipeId , равный 1. Найдя этот узел, мы двигаемся к его дочернему узлу, который называется name, и возвращаем его. Во втором выражении сначала отыскиваются все элементы name , а затем выбирается только тот, у чьего родителя имеется атрибут 1. В любом случае, вы получите один и тот же вывод (см. листинг 23).

      Листинг 23. Вывод
      Gush"gosh

      Функции

      XPath также предоставляет ряд функций. Некоторые из них относятся собственно к узлам, - например, те, которые ищут конкретную позицию, - некоторые обрабатывают строки, некоторые, - такие как суммирование, - с числами, некоторые с булевскими значениями.

      Функции, связанные с узлами

      Функции, связанные с узлами, помогают, например, выбрать конкретный узел в зависимости от позиции. Например, вы можете специально запросить последний рецепт: //recipe . Данное выражение выбирает все элементы recipe , а затем возвращает только последний. Вы также можете использовать функции отдельно, вместо того чтобы использовать их как часть предиката. Например, вы можете специально запросить посчитать элементы recipe: count(//recipe) .

      Вы уже видели функцию position() и то, как она работает. Другие функции, связанные с узлами, включают id() , local-name() , namespace-uri() и name() .

      Строковые функции

      Большинство строковых функций предназначено для обработки строк, а не для их проверки, за исключением функции contains() . Функция contains() показывает, является ли данная строка частью большего целого. Это позволит, например, вернуть только узлы, содержащие определенные строки, такие как: //recipe .

      Это выражение возвращает элемент recipe, который содержит в своем элементе name строку "breakfast".

      Функция substring() позволяет выбрать конкретный диапазон символов из строки. Например, выражение: substring(//recipe/name, 1, 5) возвращает A bal .

      Первый аргумент - это полная строка, второй - позиция первого символа, а третий - это длина диапазона.

      Среди прочих строковых функций имеются: concat() , substring-before() , substring-after() , starts-with() и string-length() .

      Числовые функции

      Числовые функции включают функцию number() , которая переводит значение в числовое, чтобы другие функции могли с ним работать. Числовые функции также включают: sum() , floor() , ceiling() и round() . Например, вы можете найти сумму всех значений recipeId при помощи выражения: sum(//recipe/@recipeId) .

      Конечно, особого повода проводить подобное вычисление нет, однако это просто числовое значение, приведенное для примера.

      Функция floor() находит наибольшее целое, которое меньше или равно данному значению, а функция ceiling() находит наименьшее целое, которое больше или равно данному значению. Функция round() работает традиционным образом - округляет (см. листинг 24).

      Листинг 24. Результаты числовых функций
      floor(42.7) = 42 ceiling(42.7) = 43 round(42.7) = 43

      Булевы функции

      Булевы функции наиболее полезны при работе с условными выражениями, о которых будет рассказано в разделе Условная обработка . Возможно, наиболее полезной функцией является not() , которая может быть использована для определения того, что определенный узел не существует. Например, выражение //recipe возвращает каждый рецепт, содержащий в элементе name строку "breakfast". Но что, если вам нужны все рецепты, кроме завтрака? Можно использовать выражение: //recipe .

      Другие булевы функции включают true() и false() , которые возвращают константы, и boolean() , которая преобразует значение в булево, чтобы его можно было использовать в качестве проверочного.

      Организация циклов и импорт

      Рассмотрим еще два важных аспекта использования таблиц стилей XSLT: создание циклов и импортирование внешних таблиц стилей.

      Организация циклов

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

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

      Листинг 25. Прямое применение стилей с использованием циклов

      Ingredients:


      Directions:

      Конструкция цикла очень похожа на структуру for-each , в честь которой она названа. Подобно тезке, каждый экземпляр цикла несет в себе следующее значение списка. В Java-программировании это могло бы быть следующее значение в массиве; здесь это следующий узел в совокупности узлов, возвращенных выражением XPath в атрибуте select . Это означает, что когда вы первый раз выполняете первый цикл, контекстным узлом является первый элемент ingredient . Это позволяет выбрать потомков этого элемента qty, unit и food и добавить их в документ так же, как это было сделано ранее при помощи шаблона. То же самое и с инструкциями, за исключением того, что они просто выводятся напрямую.

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

      Рисунок 7. Результаты

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

      Включение и импорт таблиц стилей

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

      Листинг 26. Файл ingredients.xsl

      Также можно создать отдельную таблицу стилей для инструкций (см. листинг 27).

      Листинг 27. Файл instructions.xsl
    4. Эти шаблоны идентичны шаблонам из действующей таблицы стилей. Их можно добавить к таблице стилей посредством включения (см. листинг 28).

      Листинг 28. Включение таблиц стилей
      ...

      Ingredients:

      Directions:

      Хотя здесь мы разместили таблицы стилей в той же директории, что и основную таблицу, делать это не обязательно: атрибут href может содержать любой доступный URL. Обратите внимание, что мы отсылаем процессор на поиск шаблонов для элементов ingredients и instructions , которых нет в этом файле. Однако если обработать таблицу стилей, результат получится точно тот же, как если бы шаблоны были включены напрямую, а не через элемент include (см. рисунок 8).

      Рисунок 8. Результаты

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

      XSLT позволяет импортировать таблицу стилей в начало файла. Почему в начало? Потому что целью импорта таблицы стилей является возможность корректировки любых шаблонов, являющихся частью импорта. Например, мы можем импортировать шаблон ingredients и откорректировать его (см. листинг 29).

      Листинг 29. Импорт таблицы стилей
      ... ...
    5. Здесь мы фактически заменили один шаблон двумя, которые заменяют шаблоны в импортированной таблице стилей (см. рисунок 9).

      Рисунок 9. Результаты

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

      Расширение XSLT

      Мы увидели, как сделать XSLT более похожим на программирование. Как насчет добавления к нему фактического программирования? Давайте взглянем на добавление функциональности Java к таблице стилей XSLT.

      Во-первых, следует отметить, что хотя механизм расширения является частью рекомендаций XSLT, рассматриваемая здесь реализация специфична для процессора Xalan XSLT. Основные идеи практически одинаковы для других процессоров, однако для уточнения деталей вам придется сверяться с документацией.

      Элементы расширения

      Расширение XSLT производится с помощью различных методик. Первая - это использование элементов extension . Элемент extension - это элемент в пространстве имен, который указывает на класс Java. Взгляните, например, на следующий элемент extension (см. листинг 30).

      Листинг 30. Использование элемента extension
      Recipes ...

      Мы создали пространство имен, которое соответствует классу comp.backstop.RecipeScaler , который включает в себя статический метод под названием scaleMessage (см. листинг 31).

      Листинг 31. Класс RecipeScaler
      package com.backstop; public class RecipeScaler { public static String scaleMessage (org.apache.xalan.extensions.XSLProcessorContext context, org.w3c.dom.Element thisElement){ return "This recipe has been scaled by a factor of " + thisElement.getAttribute("servings") + "."; } }

      Добравшись до элемента, процессор видит префикс пространства имен scaler: и знает, что он обозначен как префикс элемента extension, и таким образом понимает, какой класс обозначен в определении пространства имен. Вызываемый им метод отвечает локальному имени элемента - scaleMessage . Сам метод получает два аргумента, из которых мы фактически используем один. Параметр context ссылается на контекст процессора, который позволяет взглянуть на элементы, относящиеся к элементу extension , однако мы просто займемся самим элементом extension . Так как мы получаем этот элемент как параметр метода, мы можем извлечь значения любых атрибутов, добавленных к этому элементу, таких как servings в данном случае. Текст, возвращенный методом, добавляется к выводу на месте элемента extension .

      Это означает, что если применить таблицу стилей, мы получим результаты, показанные на рисунке 10.

      Рисунок 10. Результаты элемента extension

      Элементы extension могут быть весьма полезны, хотя и несколько сложны в использовании.

      Функции extension

      Еще один способ добавить функциональность при помощи таблиц стилей - это использование функций extension, которые несколько проще реализовывать, чем элементы extension. Например, мы можем создать функцию, которая умножает количество ингредиентов и количество порций (см. листинг 32).

      Листинг 32. Метод scaleIngredient()
      package com.backstop; public class RecipeScaler { public static String scaleMessage (org.apache.xalan.extensions.XSLProcessorContext context, org.w3c.dom.Element thisElement){ return "This recipe has been scaled by a factor of " + thisElement.getAttribute("servings") + "."; } public static int scaleIngredient(int servings, int original){ return (servings * original); } }

      Добавление этой функции в таблицу стилей идентично добавлению элемента extension, в котором уже есть карта пространства имен для класса (см. листинг 33).

      Листинг 33. Добавление функции extension
      ...
      ...

      Учтите, что вызов функции включает префикс пространства имен. Как и ранее, процессор видит префикс и знает, что надо выполнить вызов класса RecipeScaler . В результате количество ингредиентов умножается на два (см. рисунок 11).

      Рисунок 11. Использование функции extension

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

      Программирование XSLT

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

      Переменные XSLT

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

      Листинг 34. Задание переменной
      Recipes ...
      ...
      Рисунок 12. Использование переменной

      Обратите внимание, что ингредиенты были умножены на количество порций, как и ожидалось. Однако если вы посмотрите внимательнее, то увидите, что элемент extension сработал неверно, приняв переменную за строку, вместо того чтобы использовать значение самой переменной. Это не сбой; спецификация не требует, чтобы процессор что-либо делал со значениями атрибутов перед обработкой элемента extension. Поэтому нам надо как-то избежать этой проблемы.

      Условная обработка

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

      Листинг 35. Использование элемента if
      Recipes ...

      Содержимое элемента if , заданное атрибутом test, должно быть равно true (истина). Если это не так, что и произошло в данном случае, вывод не появится вовсе (см. рисунок 13).

      Рисунок 13. Результаты предложения if

      В том виде, в каком оно написано, предложение не имеет особого смысла; если значение больше единицы, элемент extention отобразится со значением "3." Лучше использовать множественный выбор (см. листинг 36).

      Листинг 36. Элемент choose
      Recipes Recipes have been scaled by an invalid number. Recipes have been scaled for multiple portions. ...

      В данном случае у нас имеется комбинация элементов if-then-else и предложения case из традиционных языков программирования. Элемент choose работает как контейнер, однако элемент when отображает его содержимое, только если его атрибут test равен true (истина). Наконец, если ни один из элементов when не равен true (истина), процессор отображает содержимое элемента otherwise .

      Результат получается такой, какого следует ожидать (см. рисунок 14).

      Рисунок 14. Результат

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

      Заключение

      Подведение итогов

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

      XSLT (eX tensible S tylesheet L anguage T ransformations) - язык преобразований xml-документов.

      Введение

      Задача генерирования отчетности в системе DIRECTUM является одной из наиболее востребованных.

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

      Если очень коротко, то xslt-преобразование заключается в трансформации xml-схемы с данными в отчет на основе предварительно подготовленного шаблона.

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

      Чтобы сформировать отчет понадобятся две составляющие:

      Xml-данные для отчета

      Xsl-шаблон отчета

      Данные в формате xml

      Данные в формате xml можно получить прямым sql-запросом:

      Select Analit, -- ИД записи для генерации ссылки NameAn, -- Наименование контрагента EdIzm, -- ИНН Dop2 -- Адрес from dbo.MBAnalit where Vid = %s and Sost = "Д" and XRecStat = "+" for xml path("org"), type -- Получить набор строк в виде xml

      Описанный выше запрос вернет данные в таком формате:

      101702Мобил-Авто ООО123456789 148965ОАО Тринити631000001 148966ООО Дальний восток011101001

      Данные нужно обернуть в тег и сохранить в файл, добавив заголовок:

      Вторая строка заголовка xml-файла данных важна, в ней указано, что в файле C:\Temp\template.xsl хранится шаблон отчета в который необходимо передать данные.

      В итоге получится такой xml-файл:

      101702Мобил-Авто ООО123456789426000, г. Ижевск, ул. Революционная, 44 148965ОАО Тринити631000001443000, Самарская обл., г. Самара, ул. Ленина, 11 148966ООО Дальний восток011101001100006, г. Владивосток, ул. Первая, 1

      Если открыть полученный xml-файл, браузер будет использовать указанный в заголовке шаблон и подставит данные в него.

      xsl-шаблон

      Список контрагентов

      Теперь нужно сотворить немного магии и указать какие именно данные из xml и в каком виде вставить в шаблон отчета.

      Для работы с данными в xsl-шаблоне используются специальные теги.


      Цикл for-each и правила выбора

      Чтобы перебрать все узлы из xml-файла данных нужно использовать конструкцию:

      Она позволит перебрать все дерево данных. Для того, чтобы выбрать только определенные данные из дерева используется правило выбора select="orgs/org" . Правила описаны на языке запросов XPath . В текущем примере будет последовательно отобрана каждый узел из .


      Сортировка

      Если необходимо, чтобы записи отображались не в порядке следования в xml-файле, а сортировались, то нужно указать это в шаблоне:

      Теперь записи будут перебираться предварительно отсортированными по наименованию.


      Отображение значения

      Но просто перебрать не достаточно, нужно вывести в отчет нужные данные, для этого существует конструкция:

      Конструкция отобразит значение тэга (наименование контрагента).


      Гиперссылки

      Чтобы сделать из наименования контрагента ссылку, по которой можно будет открыть запись справочника, нужно немного модифицировать код:


      Сase-оператор и фильтрация

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

      ИНН:. ИНН не указан.

      В конструкции choose с помощью фильтра test проводится проверка содержимого тэга (ИНН) на пустое значение ( ). Если содержимое не пусто, то выводиться значение. В других случаях выводится сообщение "ИНН не указан".


      Еще один фильтр

      С помощью фильтра возможно реализовать еще одну "рюшечку", которая будет помечать контрагентов с ИНН начинающихся например с кода региона "18". Выглядеть это будет так:

      Конструкция для всех записей ИНН которых начинается с "18" дополнительно выведет значок .

      В конечном итоге получится такой код шаблона:

      Список контрагентов

      ИНН:. ИНН не указан. Адрес: .


      Результат

      А если открыть xml-файл в браузере, то получится такой отчет:

      Вывод

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

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

      Какие есть плюсы при реализации отчетов описанным способом:

      Требуется разработка относительно небольшого количества прикладных вычислений;

      Разработка шаблона наглядна и также относительно нетрудоёмка.

      Всё описанное в статье легко интегрируется в аналитический отчет, ссылка на который приведена ниже.



       Top