Несмотря на то, что XSLT является вполне самостоятельным языком, его очень часто используют как составную часть в проектах, которые пишутся на других языках программирования. Тому существует множество причин. Попытаемся выделить главные из них.
□ Традиционные императивные языки программирования очень плохо подходят для обработки древовидно структурированных данных. Программы, действия в которых непременно выполняются последовательно одно за другим, в общем случае не могут эффективно (с точки зрения компактности и понятности кода) обработать сложные иерархические структуры.
□ В некоторых случаях XSLT-преобразования документов оказываются, наоборот, настолько сложны, что из соображений эффективности и простоты бывает намного легче использовать традиционные языки.
□ Во многих проектах использование XSLT может обеспечить легкую и гибкую интеграцию. Например, если одним из этапов процедуры обмена XML-данными будет XSLT-преобразование, расширение количества форматов, известных системе, будет производиться не дописыванием исходного кода, а добавлением преобразований. А поскольку XSLT обеспечивает не только синтаксические, но и семантические преобразования, то есть преобразования на структурном уровне, роль этого языка в проектах интеграции, основанных на использовании XML, может быть очень велика.
□ Использование XSLT-преобразований может коренным образом упростить создание Web-ориентированных приложений. Надо сказать, что во многих случаях XSLT-преобразования просто избавляют от необходимости программировать что-либо на других языках; однако даже тогда, когда без традиционных подходов не обойдешься, XSLT служит хорошую службу, обеспечивая простой, удобный и легко настраиваемый вывод фрагментов HTML.
В этом разделе мы приведем примеры использования преобразований в различных языках и средах разработки. Конечно же, предлагаемые программы очень просты, но и их уже должно быть достаточно, чтобы начать применять XSLT в составе своих проектов.
В этой главе мы приведем пример использования XSLT-преобразований в простом проекте, созданном в среде разработки Delphi. Базовым языком Delphi является Object Pascal. Решение, которое мы предложим, будет основываться на использовании библиотеки MSXML Parser 3.0 от Microsoft.
Небольшое приложение, которое мы создадим, будет преобразовывать XML-документ (по умолчанию — "
source.xml
") при помощи XSLT-преобразования (по умолчанию — "stylesheet.xsl
") и показывать результат преобразования.
Первым шагом после создания нового проекта (назовем его
DelphiXML
) будет импортирование библиотеки типов MSXML. Это позволит использовать в программе классы, интерфейсы и методы MSXML, в том числе и XSLT-процессор.
Для того чтобы импортировать библиотеку типов MSXML, выберем пункт меню Project/Import Type Library… (рис. 9.1).
Рис. 9.1. Импорт MSXML — шаг 1
В появившемся диалоге выберем пункт "Microsoft XML v3.0 (Version 3.0)" и создадим новый модуль кнопкой Create Unit (рис. 9.2).
Рис. 9.2. Импорт MSXML — шаг 2
Получившийся файл
MSXML2_TLB.pas
присоединим к проекту (Project/Add to Project…); теперь можно приступать к работе.
Для того чтобы использовать MSXML в нашем проекте, нам потребуется включить модуль
MSXML2_TLB
в список используемых модулей. Кроме того, для обработки исключений нам также потребуется модуль comobj
. В итоге объявление uses
будет выглядеть следующим образом:
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ComCtrls, MSXML2_TLB, comobj;
Нам понадобится форма с тремя страничками и тремя компонентами
TMemo
. В первом будет показываться исходный текст преобразуемого документа, во втором — XSLT-преобразование и в третьем — результат преобразования.
Приблизительный внешний вид формы показан на рис. 9.3.
Рис. 9.3. Внешний вид формы проекта
Объектная модель XML-документа в импортированной библиотеке будет представлена интерфейсом
DOMDocument
. В главном модуле проекта мы объявим две переменные, которые будут соответствовать обрабатываемому документу (xmlSource
) и документу преобразования (xmlStylesheet
):
var
xmlSource: DOMDocument;
xmlStylesheet: DOMDocument;
Для того чтобы создать экземпляры объектов наших документов, мы воспользуемся классом
СoDOMDocument
, который был создан в модуле MSXML2_TLB
при импортировании. Метод Create
этого класса создаст объекты, к методам и свойствам которых мы будем обращаться посредством уже упомянутого интерфейса DOMDocument
:
xmlSource := CoDOMDocument.Create;
xmlStylesheet := CoDOMDocument.Create;
Для того чтобы загрузить XML-файл, мы воспользуемся функцией
load
интерфейса DOMDocument
:
xmlSource.load('source.xml');
При загрузке файла вполне вероятны ошибки. Например, XML-документ может не являться хорошо оформленным. Для того чтобы успешно справиться с такого рода исключительными ситуациями, мы будем использовать конструкцию
try...except
и отрабатывать исключение EoleException
:
try
xmlStylesheet.load('stylesheet.xsl');
memoStylesheet.Text := xmlStylesheet.xml;
except
on e: EOleException do
memoStylesheet.Text := e.Message;
end;
Для выполнения самого преобразования нам будет нужно использовать функцию
transformNode
:
try
memoResult.Text := xmlSource.transformNode(xmlStylesheet);
except
on e: EOleException do
memoResult.Text := e.Message;
end;
Для удобства мы можем также добавить диалоги для загрузки файлов и многое другое, но эти усовершенствования мы здесь разбирать не будем. Ограничимся тем, что приведем главную часть исходного кода этого проекта.
unit source;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, MSXML2_TLB, comobj;
type
TMain = class(TForm)
{ Компоненты формы и обработчики событий }
private
public
end;
var
xmlSource: DOMDocument;
xmlStylesheet: DOMDocument;
Main: TMain;
implementation
{$R *.DFM}
procedure TMain.FormCreate(Sender: Tobject);
begin
xmlSource := CoDOMDocument.Create;
xmlStylesheet := CoDOMDocument.Create;
try
xmlSource.load('source.xml');
memoSource.Text := xmlSource.xml;
except
on e: EOleException do
memoSource.Text := e.Message;
end;
try
xmlStylesheet.load('stylesheet.xsl');
memoStylesheet.Text := xmlStylesheet.xml;
except
on e: EOleException do
memoStylesheet.Text := e.Message;
end;
end;
procedure TMain.pcMainChange(Sender: TObject);
begin
if pcMain.ActivePage = sheetResult then
try
memoResult.Text := xmlSource.transformNode(xmlStylesheet);
except
on e: EOleException do
memoResult.Text := e.Message;
end;
end;
{ Прочие процедуры и функции }
end.
Процесс использования нашего приложения приведен на следующих рисунках (рис. 9.4–9.6).
Рис. 9.4. Входящий документ
Рис. 9.5. Преобразование
Рис. 9.6. Выходящий документ
В качестве примера использования XSLT в языках С и С++ мы приведем очень простую программу, которая выполняет над документом
source.xml
преобразование stylesheet.xsl
и выводит результат в файл document.out
. На этот раз в качестве процессора мы будем использовать Xalan-C++, а в качестве среды разработки — Microsoft Visual С++.
Для того чтобы использовать библиотеки Xalan в своем проекте, прежде всего, необходимо включить в исходный код файлы заголовков:
#include "util/PlatformUtils.hpp"
#include "XalanTransformer/XalanTransformer.hpp"
Файл
PlatformUtils.hpp
относится к библиотеке Xerces-C++, который используется в Xalan в качестве парсера XML-документов. Файл заголовка XalanTransformer.hpp
относится к классу XalanTransformer
, который мы и будем использовать для преобразования нашего документа.
Заголовочные файлы Xalan и Xerces могут быть найдены в поставке Xalan в каталогах
xml-xalan\c\src
и xml-xerces\c\src
соответственно. Для того чтобы они могли быть обнаружены компилятором, эти пути следует явным образом прописать в настройках среды (меню Tools/Options), как показано на рис. 9.7.
Рис. 9.7. Настройка путей Xalan в MSVC
Для того чтобы скомпилированный объектный код мог быть скомпонован, в проекте также должны быть указаны пути к библиотечным файлам Xalan (рис. 9.8).
Рис. 9.8. Настройка путей библиотек в проекте
Теперь, когда мы разобрались со всякого рода настройками, можно заняться самой программой. Типичный сценарий использования Xalan в программе можно проиллюстрировать следующим кодом.
// Инициализируем Xerces
XMLPlatformUtils::Initialize();
// Инициализируем класс XalanTransformer
XalanTransformer::initialize();
// Создаем экземпляр класса XalanTransformer
XalanTransformer theXalanTransformer;
...
// Выполняем преобразование
theXalanTransformer.transform( ... );
...
// Освобождаем XalanTransformer
XalanTransformer::terminate();
// Освобождаем Xerces
XMLPlatformUtils::Terminate();
В соответствии с этим сценарием наша программа будет выглядеть следующим образом:
#include "StdAfx.h"
#include "util/PlatformUtils.hpp"
#include "XalanTransformer/XalanTransformer.hpp"
#include "strstream"
int main(int argc, const char* argv[]) {
using std::cerr;
// Инициализируем Xerces
XMLPlatformUtils::Initialize();
// Инициализируем класс XalanTransformer
XalanTransformer::initialize();
// Создаем экземпляр класса XalanTransformer
XalanTransformer theXalanTransformer;
// Выполняем преобразование
int theResult = theXalanTransformer.transform("source.xml",
"stylesheet.xsl", "document.out");
// В случае, если произошла ошибка, выводим, информацию о ней
if (theResult != 0) {
cerr << "XalanError: \n" << theXalanTransformer.getLastError();
}
// Освобождаем XalanTransformer
XalanTransformer::terminate();
// Освобождаем Xerces
XMLPlatformUtils::Terminate();
return theResult;
}
Начиная с четвертых версий, PHP поставляется вместе с XSLT-процессором Sablotron, который включен в РНР в качестве расширения.
Для того чтобы использовать Sablotron в PHP-скриптах, следует выполнить следующие действия:
1. Убедиться, что файл php_sablot.dll присутствует в каталоге расширений.
2. Убедиться, что в файле php.ini присутствует строка
extension=php_sablot.dll
.
3. Убедиться, что библиотеки expat.dll и sablot.dll находятся в каталоге, указанном в переменной окружения
PATH
.
Приведенное описание касается только использования Sablotron на платформе Windows32. На других платформах потребуется сконфигурировать РНР с флагом
--with-sablot
. В остальном установка совершенно аналогична.
Теперь, когда библиотека Sablotron подключена, мы сможем написать небольшую программу, которая будет выводить страницу гостевой книги.
Предположим, что мы храним (или экспортируем) данные гостевой книги в следующем формате.
18/08/2001
1
15/03/45BC
Julius
caesar@hotmail.com
:(
Et tu, Brute...
2
20/07/1969
Neil
What did I have to say? Oh, yes...
One small step for a man; one giant leap for mankind!
Для того чтобы вывести форму гостевой книги и сообщения, содержащиеся в
source.xml
, мы создадим следующее преобразование.
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Name
E-mail
Subject
From:
Subject:
Теперь займемся самим php-скриптом.
Guestbook
http-equiv="Content-Type"
content="text/html; charset=windows-1251">
// Загружаем входящий документ
$sourcefile = "source.xml";
$sourcehandle = fopen($sourcefile, "r")
or die("Невозможно открыть входящий документ.");
$source = fread($sourcehandle, filesize($sourcefile));
// Загружаем преобразование
$stylesheetfile = "stylesheet.xsl";
$stylesheethandle = fopen($stylesheetfile, "r")
or die("Невозможно открыть файл преобразования");
$stylesheet = fread($stylesheethandle, filesize($stylesheetfile));
// Инициализируем XSLT-процессор
$xslt = @xslt_create() or die("Can't create XSLT handle!");
// Выполняем преобразование
@xslt_process($stylesheet, $source, $result);
// Выводим результат
echo $result;
// Освобождаем ресурсы
@xslt_free($xslt);
?>
Приблизительный результат выполнения этого скрипта можно видеть на рис. 9.9.
Рис. 9.9. Сгенерированная из PHP-скрипта страница гостевой книги
JavaScript является одним из наиболее популярных скриптовых языков, которые применяются при программировании для Web. В этой главе мы покажем, как при помощи JavaScript и MSXML создать интерактивный каталог, основанный на XML и XSLT.
Предположим, что каталог организован в виде иерархии категорий приблизительно следующим образом.
При отображении этого дерева мы будем раскрывать только определенную выбранную ветвь категорий. Скажем, если пользователь выбрал категорию "Расходные материалы", показывать информацию о компьютерах мы ему не будем. Иными словами, мы будем показывать только те категории, которые являются надкатегориями выбранной. Для того чтобы сделать это как можно эффективнее, мы выполним следующие шаги.
□ При помощи ключа и уникального идентификатора, сгенерированного функцией
generate-id
, мы найдем в дереве требуемую категорию и присвоим ее переменной $category
.
□ Воспользовавшись осью
ansector-or-self
, мы найдем все надкатегории данной, то есть все категории, которые прямо или косвенно содержат найденную. Путь выборки будет иметь вид $category/ancestor-or-self::category
. Найденное множество мы присвоим переменной $path
.
□ При обработке каждой из категорий мы будем обрабатывать ее подкатегории только в том случае, если она является надкатегорией выбранной; иначе говоря — только в том случае, когда ее узел принадлежит множеству узлов
$path
. Проверять это мы будем при помощи условия count(.|$path)=count($path)
.
Искомое преобразование в итоге запишется в виде.
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
select="$category/ancestor-or-self::category"/>
<а href="javascript:expand('{generate-id(.)}')">
images/dot.gif
images/minus.gif
images/plus.gif
select="concat($indent,' ')"/>
Теперь осталось лишь только создать страницу, которая при помощи JavaScript и MSXML будет выполнять преобразования и выводить результат.
Для того чтобы воспользоваться возможностями MSXML, мы включим в нашу страницу два объекта:
id="source"
width="0"
height="0"
classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
id="stylesheet"
width="0"
height="0"
classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
"Магический" код
clsid:f5078f32-c551-11d3-89b9-0000f81fe221
, который присутствует в тегах обоих объектов, на самом деле не что иное, как уникальный идентификатор библиотеки MSXML 3.0, которую мы и будем использовать для выполнения преобразования. Итак, код нашей HTML- страницы будет выглядеть следующим образом.
http-equiv="Content-Type"
content="text/html; charset=windows-1251" />
body {font-family:Tahoma,Verdana,Arial,sans-serif; font-size:14px}
a:link {COLOR:#990000; BACKGROUND: #ffffff; TEXT-DECORATION: none}
a:hover {BACKGROUND: #dddddd; TEXT-DECORATION: none}
a:visited {COLOR: #990000; TEXT-DECORATION: none}
id="source"
width="0"
height="0"
classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
id="stylesheet"
width="0"
height="0"
classid="clsid:f5078f32-c551-11d3-89b9-0000f81fe221">
Каталога>
В браузере эта страница будет выглядеть следующим образом (рис. 9.10).
Рис. 9.10. Динамический каталог на HTML с использованием JavaScript, MSXML на основе XML и XSLT
Использование MSXML на стороне сервера не сильно отличается от клиентской версии, которую мы разобрали выше. Поскольку MSXML является стандартным СОМ-объектом, его можно использовать в любом языке программирования, умеющем работать с COM. В следующем примере будет показано, как можно использовать MSXML в ASP-странице, написанной на языке VBScript. Мы напишем небольшое Web-приложение, которое позволит отправлять короткие сообщения (SMS) через разные службы, используя один интерфейс.
Почти у всех операторов мобильной связи формы для отправки сообщений более или менее стандартны, например:
При этом различаться могут адреса служб отправки сообщений, методы отправки форм и наименования полей ввода. Все это мы можем описать в отдельном документе.
http://www.mtnsms.com/sendsms.php
GET
msg
num
http://www.smshost.net/servlets/sms
POST
message
phone
Контакт-лист после этого может быть оформлен следующим образом.
Иван Иванович
18005557684
Иван Никифорович
447856273447
Преобразование, генерирующее HTML-страницу с формой отправки можно задать как.
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transfоrm">
method="html"
indent="yes"
encoding="windows-1251"/>
[хА0;
]
хА0;хА0;
select="document('services.xml')/services/
service[@id = current()/service/@id]"/>
action="{$service/action}" method="{$service/method}">
name="{$service/number}"
value="{number}"/>
name="{$service/text}"/>
SMS Center
Теперь дело осталось за ASP-страницей, которая применяла бы преобразование
stylesheet.xsl
к документу source.xml
и возвращала результат клиенту.
<%@ LANGUAGE = VBScript %>
<%
' Загружаем входящий документ
Dim source
Set source = Server.CreateObject("MSXML2.FreeThreadedDOMDocument.3.0")
source.load Server.MapPath("source.xml")
' Загружаем преобразование
Dim stylesheet
Set stylesheet =
Server.CreateObject("MSXML2.FreeThreadedDOMDocument.3.0")
stylesheet.load Server.MapPath("stylesheet.xsl")
' Создаем объект XSLTemplate для преобразования
Dim templates
Set templates = Server.CreateObject("MSXML2.XSLTemplate")
templates.stylesheet = stylesheet.documentElement
' Создаем объект XSLT-процессора
Dim processor
Set processor = templates.createProcessor
processor.input = source
' Присваиваем параметру id значение параметра запроса id
' (то, что передано в sms.asp?id=...)
processor.addParameter "id", "" + Request.QueryString("id"), ""
' Выполняем преобразование
processor.transform
' Возвращаем результат
Response.Charset = "windows-1251"
Response.Write processor.output
%>
На рис. 9.11 показаны результаты работы
sms.asp
для id=p1
и id=p2
.
Рис. 9.11. Внешний вид страницы, возвращаемой
sms.asp
При вызове страницы
sms.asp
или sms.asp?id=p1
форма отправки сообщений будет сгенерирована в следующем виде:
Для
sms.asp?id=p2
форма будет иметь вид:
Пример использования XSLT-преобразований в Python, который мы продемонстрируем ниже, будет основываться на использовании библиотек 4Suite и PyXML.
Простейший скрипт, преобразующий документ
source.xml
при помощи преобразования stylesheet.xsl
будет выглядеть следующим образом.
python -с "import sys;from xml.xslt import _4xslt;_4xslt.Run(sys.argv[1:])" -i source.xml stylesheet.xsl
Использование XSLT-процессора в собственных программах на Python ненамного сложнее.
# Импортируем библиотеки
import sys
from xml.xslt.Processor import Processor
# Создаем XSLT-процессор
processor = Processor()
# Загружаем XSLT-преобразование
processor.appendStylesheetUri('stylesheet.xsl')
# Выполняем преобразование
result = processor.runUri('source.xml')
# Выводим результирующий документ print result
Универсальность технологии XSLT позволяет использовать ее на самых различных уровнях архитектуры приложений. В этом разделе мы приведем пример использования преобразований внутри базы данных.
На этот раз в качестве целевой платформы будет использоваться база данных Oracle 8i, которая обеспечивает поддержку XSLT несколькими встроенными пакетами:
XMLDOM
, XMLPARSER
и XSLPROCESSOR
.
Представим себе следующую схему элементарной БД (рис. 9.12):
Рис. 9.12. Схема простой базы данных
Таблица STYLESHEET содержит XSLT-преобразования, которые хранятся в полях
CONTENT
, поле ID
указывает уникальный идентификатор каждого из них.
Таблица
SOURCE
содержит XML-документы (поле CONTENT
), каждому из которых соответствует некоторое преобразование (внешний ключ STYLESHEETID
). Нашей задачей будет создание представления, в котором документы, хранящиеся в таблице SOURCE
, будут обрабатываться соответствующими преобразованиями из таблицы STYLESHEET
.
Прежде всего, создадим таблицы и ключи, соответствующие приведенной выше схеме базы данных.
-- Создаем таблицу stylesheet
CREATE TABLE STYLESHEET
(ID INTEGER NOT NULL,
CONTENT CLOB NULL);
-- Создаем первичный ключ таблицы STYLESHEET
ALTER TABLE STYLESHEET
ADD (PRIMARY KEY (ID));
-- Создаем таблицу SOURCE
CREATE TABLE SOURCE
(ID INTEGER NOT NULL,
CONTENT CLOB NULL,
STYLESHEETID INTEGER NOT NULL);
-- Создаем первичный ключ таблицы SOURCE
ALTER TABLE SOURCE
ADD (PRIMARY KEY (ID));
-- Создаем внешний ключ, связывающий таблицы SOURCE и STYLESHEET
ALTER TABLE SOURCE
ADD (FOREIGN KEY (STYLESHEETID) REFERENCES STYLESHEET);
После того, как схема базы данных была создана, в нее можно добавить записи, содержащие преобразования и обрабатываемые ими документы. Мы ограничимся простым преобразованием и еще более простым документом.
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
value
-- Сохраняем преобразование
INSERT INTO STYLESHEET VALUES
(1, '
', version="1.0" '||
' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> '||
' '||
' '||
' '||
' ');
-- Сохраняем документ
INSERT INTO SOURCE VALUES
(1, 'value', 1);
Для того чтобы выполнять преобразования в
SELECT
-выражении представления таблицы SOURCE
, мы напишем функцию PROCESS
, которая будет возвращать результат обработки документа с уникальным идентификатором, заданным параметром sourceID
.
CREATE OR REPLACE FUNCTION PROCESS (sourceID NUMBER) RETURN VARCHAR2 IS
-- Инициализация XML-парсера и XSLT-процессора
parser XMLPARSER.Parser := XMLPARSER.newParser;
processor XSLPROCESSOR.Processor := XSLPROCESSOR.newProcessor;
-- Переменные для CLOB-значений входящего документа и преобразования
sourceCLOB CLOB;
stylesheetCLOB CLOB;
-- Переменные для DOM-объектов входящего документа и преобразования
sourceXML XMLDOM.DOMDocument;
stylesheetXML XMLDOM.DOMDocument;
-- Переменная для объекта преобразования
stylesheet XSLPROCESSOR.Stylesheet;
-- Переменная результата
result varchar2(32767);
BEGIN
-- Получаем CLOB-значение входящего документа в переменную sourceCLOB
SELECT CONTENT
INTO sourceCLOB
FROM SOURCE
WHERE ID = sourceID;
-- Получаем CLOB-значение соответствующего преобразования
SELECT STYLESHEET.CONTENT
INTO stylesheetCLOB
FROM STYLESHEET, SOURCE
WHERE SOURCE.ID = sourceID AND SOURCE.STYLESHEETID = STYLESHEET.ID;
-- Если хотя бы одно из значений - NULL, прерываем обработку
-- и возвращаем NULL
IF sourceCLOB IS NULL OR stylesheetCLOB IS NULL THEN
RETURN NULL;
END IF;
-- Разбираем CLOB-значение входящего документа
XMLPARSER.parseCLOB(parser, sourceCLOB);
sourceXML := XMLPARSER.getDocument(parser);
-- Разбираем CLOB-значение документа преобразования
XMLPARSER.parseCLOB(parser, stylesheetCLOB);
stylesheetXML := XMLPARSER.getDocument(parser);
-- Инициализируем объект преобразования
stylesheet := XSLPROCESSOR.newStylesheet(stylesheetXML, NULL);
-- Выполняем преобразование
XSLPROCESSOR.processXSL(processor, stylesheet, sourceXML, result);
-- Освобождаем ресурсы
XSLPROCESSOR.freeProcessor(processor);
XMLPARSER.freeParser(parser);
XMLDOM.freeDocument(sourceXML);
XMLDOM.freeDocument(stylesheetXML);
RETURN result;
-- Обработка исключений
EXCEPTION
-- Если возникла исключительная ситуация
WHEN OTHERS THEN
-- Освобождаем ресурсы
XSLPROCESSOR.freeProcessor(processor);
XMLPARSER.freeParser(parser);
XMLDOM.freeDocument(sourceXML);
XMLDOM.freeDocument(stylesheetXML);
-- Передаем исключение дальше
RAISE;
END;
Представление обработанных документов теперь может быть описано совершенно элементарно.
CREATE OR REPLACE VIEW PROCESSED_SOURCE AS
SELECT ID, PROCESS(ID) AS CONTENT
FROM SOURCE;
Продемонстрируем работу функции
PROCESS
и представления PROCESS_SOURCE
на примере двух запросов.
SQL> SELECT * FROM SOURCE;
ID CONTENT STYLESHEETID
-- ------------ ------------
1 value 1
SQL> SELECT * FROM PROCESSED_SOURCE;
ID CONTENT
-- -------------------------------------------------------
1 value
Язык Java традиционно широко поддерживает XML-технологии: большинство передовых разработок в этой области реализуется, как правило, сначала на Java и уж затем переносится на другие платформы разработки.
Не стал исключением и XSLT. Можно смело сказать, что количество XSLT-средств, написанных на Java, превосходит половину вообще всех существующих в настоящее время XSLT-пакетов.
Для того чтобы продемонстрировать использование XSLT в Java, мы приведем два варианта одной и той же программы — серверного приложения (сервлета), которое по запросу клиента будет возвращать информацию о текущем HTTP-сеансе в формате HTML.
Первый вариант сервлета можно назвать "традиционным". В нем HTML-документ создается серией инструкций
out.println(...)
, которые выводят в выходящий поток размеченную HTML-тегами информацию о текущем сеансе.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
public class example extends HttpServlet {
/**
* Инициализация.
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
/**
* Основной метод сервлета
*/
public void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Выставляем тип содержимого
response.setContentType("text/html");
// Инициализируем выходящий поток
OutputStreamWriter osw =
new OutputStreamWriter(response.getOutputStream());
PrintWriter out = new PrintWriter (response.getOutputStream());
// Выполняем вывод HTML-страницы
out.println("");
// Выводим головную часть HTML-документа
out.println(" ");
out.println(" Request information ");
out.println(" ");
// Выводим тело документа
out.println(" ");
// Выводим общую информацию о запросе
out.println(" General information");
out.println(" ");
// Выводим имя сервера
out.println(" ");
out.println(" Server name ");
out.println(" " + request.getServerName() + " ");
out.println("
");
// Выводим порт сервера
out.println(" ");
out.println(" Server port ");
out.println(" " + request.getServerPort() + " ");
out.println("
");
// Выводим адрес запрашивающей стороны
out.println(" ");
out.println(" Remote address ") ;
out.println(" " + request.getRemoteAddr() + " ");
out.println("
");
// Выводим название протокола запроса
out.println(" ");
out.println(" Protocol ");
out.println(" " + request.getProtocol() + " ");
out.println("
");
// Выводим метод запроса
out.println(" ") ;
out.println(" Method ");
out.println(" " + request.getMethod() + " ");
out.println("
");
// Выводим URI запроса
out.println(" ");
out.println(" Request URI ");
out.println(" " + request.getRequestURI() + " ");
out.println("
");
// Выводим строку запроса
out.println(" ");
out.println(" Query String ");
out.println(" " + request.getQueryString() + " ");
out.println("
");
out.println("
");
// Выводим параметры запроса
out.println(" Request parameters");
out.println(" ");
for (Enumeration e = request.getParameterNames();
e.hasMoreElements();) {
String name = e.nextElement().toString();
String[] values = request.getParameterValues(name);
for (int i=0; i < values.length; i++) {
out.println(" ");
out.println(" " + name + " ");
out.println(" " + values[i] + " ");
out.println("
");
}
}
out.println("
");
// Выводим параметры HTTP-сессии
out.println(" Session parameters");
out.println(" ");
HttpSession session = request.getSession(true);
String[] names = session.getValueNames();
for (int i=0; i < names.length; i++) {
String name = session.getValueNames()[i];
out.println(" ");
out.println(" " + name + " ");
out.println(" " +
session.getValue(name).toString() + "
");
out.println("
");
}
out.println("
");
// Выводим cookies
response.addCookie(new Cookie("content", "apple jam"));
out.println(" Cookies");
out.println(" ");
Cookie[] cookies = request.getCookies();
for (int i=0; i < cookies.length; i++) {
out.println(" ");
out.println(" " + cookies[i].getName() + " ");
out.println(" " + cookies[i].getValue() + " ");
out.println("
");
}
out.println("
");
out.println(" ");
out.println("");
// Закрываем выходящий поток
out.close();
}
}
Результатом обращения к этому сервлету по URL вида
http://localhost/servlet/example?x=1&y=2&z=3&x=4&y=5&z=6
будет документ, аналогичный представленному на рис. 9.13.
Рис. 9.13. Результат обращения к сервлету
Несложно видеть, насколько жестко в этом сервлете закодирована презентация данных: для минимального изменения генерируемого документа придется в обязательном порядке изменять сам сервлет, что в современных системах может быть непозволительной роскошью, — все равно, что перебирать мотор для того, чтобы перекрасить автомобиль.
Второй вариант того же самого сервлета, который мы предложим ниже, демонстрирует, как в данном случае при помощи XSLT можно разделить данные и их презентацию. Идея очень проста: вместо того, чтобы в жестко заданном виде выводить информацию в выходящий поток, можно создать XML-документ в виде DOM-объекта и затем применить к нему XSLT-преобразование, которое создаст для него требуемое HTML-представление.
В этом варианте сервлета мы будем использовать Java-версию XML-библиотеки Oracle XDK (Oracle XML SDK, платформа разработки XML-приложений, созданная в Oracle Corp.). В данном примере из этой библиотеки мы будем использовать только XSLT-процессор (класс
XSLProcessor
) и реализацию DOM-модели XML-документа (класс XMLDocument
). Во всем остальном мы будем полагаться на Java-реализацию стандартных интерфейсов объектной модели документа DOM, разработанной Консорциумом W3. DOM-интерфейсы позволят нам манипулировать XML-документом на уровне модели: создавать и включать друг в друга узлы элементов, текстовые узлы и так далее.
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.net.*;
import oracle.xml.parser.v2.*;
import org.w3c.dom.*;
public class example extends HttpServlet {
/**
* Функция, создающая в элементе parent элемент с именем name и
* текстовым значением value. Если value имеет значение null,
* текст не создается.
*/
public static Element addElement(Element parent, String name, String value) {
Element child = parent.getOwnerDocument().createElement(name);
parent.appendChild(child);
if (value != null) {
Text text = parent.getOwnerDocument().createTextNode(value);
child.appendChild(text);
}
return child;
}
/**
* Инициализация.
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
/**
* Основной метод сервлета
*/
public void service(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
// Выставляем тип содержимого
response.setContentType("text/html");
// Инициализируем выходящий поток
OutputStreamWriter o_sw =
new OutputStreamWriter(response.getOutputStream());
PrintWriter out = new PrintWriter(response.getOutputStream());
// Получаем объекты
cookie Cookie[] cookies = request.getCookies();
// Создаем выходящий документ
XMLDocument doc = new XMLDocument();
// Создаем корневой элемент
Request Element elRequest = doc.createElement("Request");
doc.appendChild(elRequest);
// Создаем элемент General
Element elGeneral = addElement(elRequest, "General", null);
// Создаем элементы, содержащие общую информацию
addElement(elGeneral, "ServerName", request.getServerName());
addElement(elGeneral, "ServerPort",
Integer.toString(request.getServerPort()));
addElement(elGeneral, "RemoteAddr", request.getRemoteAddr());
addElement(elGeneral, "Protocol", request.getProtocol());
addElement(elGeneral, "Method", request.getMethod());
addElement(elGeneral, "RequestURI", request.getRequestURI());
addElement(elGeneral, "QueryString", request.getQueryString());
// Создаем элемент Param
Element elParam = addElement(elRequest, "Param", null);
// В элементе Param создаем элементы, описывающие параметры запроса
for (Enumeration e = request.getParameterNames();
e.hasMoreElements();) {
String name = e.nextElement().toString();
String[] values = request.getParameterValues(name);
// Для каждого из значений каждого из параметров
// создаем соответствующий элемент
for (int i=0; i < values.length; i++)
addElement(elParam, name, values[i]);
}
// Создаем элемент Session
Element elSession = addElement(elRequest, "Session", null);
// Получаем объект HTTP-сессии
HttpSession session = request.getSession(true);
// Получаем имена параметров сессии
String[] names = session.getValueNames();
// В элементе Session создаем по элементу
// для каждого из параметров сессии
for (int i=0; i < names.length; i++)
addElement(elSession, session.getValueNames()[i],
session.getValue(session.getValueNames()[i]).toString());
// Создаем элемент Cookie
Element elCookie = addElement(elRequest, "Cookie", null);
// Создаем по элементу для каждого из объектов cookies
for (int i=0; i < cookies.length; i++)
addElement(elCookie, cookies[i].getName(), cookies[i].getValue());
// Преобразовываем созданный документ и выводим результат
try {
// Загружаем преобразование
XSLStylesheet stylesheet = new XSLStylesheet(
new URL("http://localhost/stylesheet.xsl"), null);
// Выполняем преобразование
XMLDocumentFragment fragment =
(XMLDocumentFragment)doc.transformNode(stylesheet);
// Выводим результат
fragment.print(out);
}
catch (MalformedURLException mue) {}
catch (XSLException xsle) {}
// Закрываем выходящий поток
out.close();
}
}
В этом сервлете вместо того, чтобы просто печатать в выходящий поток данные и HTML-разметку, в переменной
doc
мы генерируем DOM-объект XML-документа. После того как все текстовые узлы и узлы элементов будут сгенерированы, документ, содержащийся в переменной doc
, примет приблизительно следующий вид.
aphrodite.fzi.de
80
127.0.0.1
HTTP/1.1
GET
/servlet/example1
x=1&y=2&z=3&x=4&y=5&z=6
3
6
2
5
1
4
4
apple jam
aaenbyjqc0
После того как генерация документа завершена, к нему применяется преобразование
stylesheet.xsl
, которое создает его HTML-представление.
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
Request information
General information
Request parameters
Session parameters
Cookies
Server name
Server port
Remote address
Request URI
Query string
Результатом этого преобразования является следующий HTML-документ, внешний вид которого полностью идентичен документу, показанному на рис. 9.13.
Request information
General information
Server name
aphrodite.fzi.de
Server port
80
Remote address
127.0.0.1
Protocol
HTTP/1.1
Method
GET
Request URI
/servlet/example1
Query string
x=1&y=2&z=3&x=4&y=5&z=6
Request parameters
z
3
z
6
y
2
y
5
x
1
x
4
Session parameters
v
4
Cookies
content
apple jam
JServSessionIdroot
aaenbyjqc0
Второй вариант сервлета, конечно, не проще, чем первый, да и вряд ли он будет быстрее и экономичнее с точки зрения памяти, ведь вместо простого вывода текста в поток мы сначала создаем в памяти объектную модель документа, преобразуем ее и только затем выводим результат. Однако главное, чего удалось в этом случае добиться, — это отделение данных от их презентации.
Представим, к примеру, что нам потребовалось перевести названия полей выводимого документа на русский язык — получить текст "
Общая информация
" вместо "General information
" и так далее. В первом случае для внесения этого элементарного представления потребуется переписывать, перекомпилировать и обновлять на сервере сервлет; во втором случае все, что нужно будет сделать, — это исправить несколько строк в файле stylesheet.xsl
.
В заключение хотелось бы сделать несколько комментариев относительно применения XSLT и вообще XML-технологий.
Как и в любом другом случае, нужно очень тщательно взвешивать целесообразность применения в проекте тех или иных средств. К сожалению, шумиха вокруг XML имеет чисто коммерческий характер, маркетинговые службы часто выдают желаемое за действительное, объявляя XML серебряной пулей для всех проблем информационных технологий.
Как мы знаем, серебряных пуль не бывает. Нужно всегда очень трезво относиться к выбору технологий, хорошо понимая их плюсы, минусы и что каждый из этих знаков будет означать для конкретного проекта. Глупо вслепую следовать моде и тенденциям, не обращая внимания на возникающие при этом издержки.
С этих позиций XSLT является наименее проблемной технологией в том смысле, что если встает вопрос, использовать XSLT или нет, это уже означает: вопрос об использовании XML-технологий решен положительно. Значит, разработчики уже пошли на жертвы ресурсов памяти и процессорной мощности, которые XSLT вряд ли ужесточит. Иначе говоря, аппаратные требования не являются определяющими для использования XSLT.
Другое обстоятельство, которое необходимо принимать во внимание, — это сложность самого преобразования. Базовый набор элементов XSLT вкупе с расширениями уже представляется чрезвычайно мощным средством для выполнения различных преобразований, однако в некоторых случаях даже этого может быть недостаточно. В других случаях мощь XSLT может наоборот оказаться неоправданной — например, с задачей представления внешнего вида HTML-документа в Web-браузере могут великолепно справиться каскадные таблицы стилей (CSS).