Технология XSLT

Валиков Алексей Н.

Глава 9

Совместное использование XSLT с другими языками программирования

 

 

XSLT и другие языки

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

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

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

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

□ Использование XSLT-преобразований может коренным образом упростить создание Web-ориентированных приложений. Надо сказать, что во многих случаях XSLT-преобразования просто избавляют от необходимости программировать что-либо на других языках; однако даже тогда, когда без традиционных подходов не обойдешься, XSLT служит хорошую службу, обеспечивая простой, удобный и легко настраиваемый вывод фрагментов HTML.

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

 

Выполнение XSLT-преобразований в Object Pascal

 

В этой главе мы приведем пример использования XSLT-преобразований в простом проекте, созданном в среде разработки Delphi. Базовым языком Delphi является Object Pascal. Решение, которое мы предложим, будет основываться на использовании библиотеки MSXML Parser 3.0 от Microsoft.

Небольшое приложение, которое мы создадим, будет преобразовывать XML-документ (по умолчанию — "source.xml") при помощи XSLT-преобразования (по умолчанию — "stylesheet.xsl") и показывать результат преобразования.

 

Импорт MSXML в Delphi

Первым шагом после создания нового проекта (назовем его 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. Внешний вид формы проекта

 

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

DOMDocument

Объектная модель 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;

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

Листинг 9.1. Использование XSLT-преобразования в Delphi

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-преобразований в C/C++

 

В качестве примера использования 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. Настройка путей библиотек в проекте

 

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

XalanTransformer

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

Листинг 9.2. Типовой сценарий использования 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;

}

 

Выполнение XSLT-преобразований в PHP

 

Начиная с четвертых версий, 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 подключена, мы сможем написать небольшую программу, которая будет выводить страницу гостевой книги.

 

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

Предположим, что мы храним (или экспортируем) данные гостевой книги в следующем формате.

Листинг 9.3. Данные гостевой книги — файл source.xml

 18/08/2001

 

 

   1

   15/03/45BC

   Julius

   [email protected]

   :(

   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, мы создадим следующее преобразование.

Листинг 9.4. Преобразование stylesheet.xsl

 version="1.0"

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

 

 

 

 

  

 

  

   

    

    

   

    

     

    

   

   

    

  

  

 

 

 

 

 

 

  Server name

 

 

  Server port

 

 

  Remote address

 

 

  Request URI

 

 

  Query string

 

Результатом этого преобразования является следующий HTML-документ, внешний вид которого полностью идентичен документу, показанному на рис. 9.13.

Листинг 9.26. Результирующий HTML-документ

 

  Request information

 

 

 

General information

  

Name E-mail

      Subject

      

     

     

     

 

 

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

Листинг 9.9. Документ, описывающий формы служб отправки сообщений — services.xml

 

  http://www.mtnsms.com/sendsms.php

  GET

  msg

  num

 

 

  http://www.smshost.net/servlets/sms

  POST

  message

  phone

 

Контакт-лист после этого может быть оформлен следующим образом.

Листинг 9.10. Контакт-лист — документ source.xml

 

  Иван Иванович

  18005557684

 

 

 

  Иван Никифорович

  447856273447

 

 

Преобразование, генерирующее HTML-страницу с формой отправки можно задать как.

Листинг 9.11. Преобразование stylesheet.xsl

 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}"/>

  

 

 

Для sms.asp?id=p2 форма будет иметь вид:

 

 

 

 

 

Выполнение XSLT-преобразований в Python

Пример использования XSLT-преобразований в Python, который мы продемонстрируем ниже, будет основываться на использовании библиотек 4Suite и PyXML.

Простейший скрипт, преобразующий документ source.xml при помощи преобразования stylesheet.xsl будет выглядеть следующим образом.

Листинг 9.13. Простейший вызов 4Suite

python -с "import sys;from xml.xslt import _4xslt;_4xslt.Run(sys.argv[1:])" -i source.xml stylesheet.xsl

Использование XSLT-процессора в собственных программах на Python ненамного сложнее.

Листинг 9.14. Использование 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-преобразований в PL/SQL

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

На этот раз в качестве целевой платформы будет использоваться база данных Oracle 8i, которая обеспечивает поддержку XSLT несколькими встроенными пакетами: XMLDOM, XMLPARSER и XSLPROCESSOR.

Представим себе следующую схему элементарной БД (рис. 9.12):

Рис. 9.12. Схема простой базы данных

Таблица STYLESHEET содержит XSLT-преобразования, которые хранятся в полях CONTENT, поле ID указывает уникальный идентификатор каждого из них.

Таблица SOURCE содержит XML-документы (поле CONTENT), каждому из которых соответствует некоторое преобразование (внешний ключ STYLESHEETID). Нашей задачей будет создание представления, в котором документы, хранящиеся в таблице SOURCE, будут обрабатываться соответствующими преобразованиями из таблицы STYLESHEET.

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

Листинг 9.15. Создание схемы БД

-- Создаем таблицу 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);

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

Листинг 9.16. Преобразование

 version="1.0"

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

 

 

 

Листинг 9.17. Обрабатываемый документ

value

Листинг 9.18. SQL-скрипт, загружающий документ и преобразование в БД

-- Сохраняем преобразование

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.

Листинг 9.19. Функция PROCESS

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;

Представление обработанных документов теперь может быть описано совершенно элементарно.

Листинг 9.20. Представление PROCESSED_SOURCE

CREATE OR REPLACE VIEW PROCESSED_SOURCE AS

SELECT ID, PROCESS(ID) AS CONTENT

FROM SOURCE;

Продемонстрируем работу функции PROCESS и представления PROCESS_SOURCE на примере двух запросов.

Листинг 9.21. Запросы к таблице SOURCE и представлению PROCESSED_SOURCE

SQL> SELECT * FROM SOURCE;

ID  CONTENT       STYLESHEETID

--  ------------  ------------

1   value  1

SQL> SELECT * FROM PROCESSED_SOURCE;

ID CONTENT

-- -------------------------------------------------------

value

 

Выполнение XSLT-преобразований в Java

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

Не стал исключением и XSLT. Можно смело сказать, что количество XSLT-средств, написанных на Java, превосходит половину вообще всех существующих в настоящее время XSLT-пакетов.

Для того чтобы продемонстрировать использование XSLT в Java, мы приведем два варианта одной и той же программы — серверного приложения (сервлета), которое по запросу клиента будет возвращать информацию о текущем HTTP-сеансе в формате HTML.

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

Листинг 9.22. Традиционный вариант сервлета

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("   

");

  out.println("   

");

  out.println("  

");

  // Выводим порт сервера

  out.println("  

");

  out.println("   

");

  out.println("   

");

  out.println("  

");

  // Выводим адрес запрашивающей стороны

  out.println("  

");

  out.println("   

") ;

  out.println("   

");

  out.println("  

");

  // Выводим название протокола запроса

  out.println("  

");

  out.println("   

");

  out.println("   

");

  out.println("  

");

  // Выводим метод запроса

  out.println("  

") ;

  out.println("   

");

  out.println("    

");

  out.println("  

");

  // Выводим URI запроса

  out.println("  

");

  out.println("   

");

  out.println("   

");

  out.println("  

");

  // Выводим строку запроса

  out.println("  

");

  out.println("   

");

  out.println("   

");

  out.println("  

");

  out.println("  

Server name " + request.getServerName() + "
Server port " + request.getServerPort() + "
Remote address " + request.getRemoteAddr() + "
Protocol " + request.getProtocol() + "
Method " + request.getMethod() + "
Request URI " + request.getRequestURI() + "
Query String " + request.getQueryString() + "
");

  // Выводим параметры запроса

  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("   

");

    out.println("   

");

    out.println("  

");

   }

  }

  out.println(" 

" + name + " " + values[i] + "
");

  // Выводим параметры 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("   

");

   out.println("   

");

   out.println("  

");

  }

  out.println(" 

" + name + " " +

   session.getValue(name).toString() + "

");

  // Выводим 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("   

");

   out.println("   

");

   out.println("  

");

  }

  out.println(" 

" + cookies[i].getName() + " " + cookies[i].getValue() + "
");

  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-документом на уровне модели: создавать и включать друг в друга узлы элементов, текстовые узлы и так далее.

Листинг 9.23. Вариант сервлета, использующий XSLT

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, примет приблизительно следующий вид.

Листинг 9.24. XML-документ, сгенерированный в сервлете

 

  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-представление.

Листинг 9.25. Преобразование stylesheet.xsl

 version="1.0"

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

 

  

  

    Request information

  

  

  

 

 

 

 

 

 

  General information

 

 

  Request parameters

 

 

  Session parameters

 

 

  Cookies

 

 

 

  

   

   

  

  

   

   

  

  

   

   

  

  

   

   

  

  

   

   

  

  

   

   

  

  

   

   

  

 

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).