XSLT процветает с Java

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

Многие процессоры XSLT допускают какой-либо механизм расширения; спецификация требует от них этого. В мире Java и XML наиболее широко используемым процессором XSLT является процессор Apache Xalan с открытым исходным кодом. Написанный на Java, Xalan допускает расширения на Java. Многие разработчики считают расширяемость Xalan мощной, поскольку она позволяет им использовать свои навыки работы с Java из контекста таблицы стилей. Рассмотрим, как JSP (страницы JavaServer), скриптлеты и пользовательские теги добавляют возможности HTML. Расширения Xalan добавляют возможности таблицам стилей почти таким же образом: разрешая разработчикам Java доступ к их любимому инструменту - Java.

В этой статье я продемонстрирую, как можно использовать Java из таблицы стилей XSLT. Во-первых, мы будем использовать расширяемость Xalan для создания экземпляров и использования существующих классов в JDK. Позже я покажу вам, как написать функцию расширения XSLT, которая принимает Stringаргумент и возвращает фрагмент DOM (объектной модели документа) процессору таблицы стилей.

XSLT важен для разработчиков J2EE (Java 2 Platform, Enterprise Edition), потому что стилизация XML-документов стала операцией на стороне сервера. Кроме того, JAXP (API Java для обработки XML), который включает поддержку механизмов XSLT, стал частью спецификации J2EE (J2EE 2.6.11). В начальной стадии XSLT предназначался для стилизации XML на клиенте; однако большинство приложений стилизуют XML перед его отправкой клиенту. Для разработчиков J2EE это означает, что процессор XSLT, скорее всего, будет работать на сервере приложений.

Прежде чем продолжить работу с этой статьей, имейте в виду, что использование расширений Java в таблицах стилей XSLT снижает их переносимость. Хотя расширения являются частью спецификации XSLT, способ их реализации - нет. Если ваши таблицы стилей будут работать на процессорах, отличных от Xalan, таких как движок таблиц стилей Internet Explorer, вам следует избегать использования расширений любой ценой.

Слабые стороны XSLT

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

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

Предположим, ваш босс просит вас изменить таблицу стилей, чтобы она преобразовывала все экземпляры «не является» в «не является» и локализовала общие метки. Конечно, XSLT предоставляет механизм, чтобы делать что-то в этом направлении, верно? Неправильно. XSLT не предоставляет простого способа заменить вхождение слова или шаблона в строке. То же самое и с локализацией. Это не значит, что этого нельзя сделать с помощью стандартного синтаксиса XSLT. Есть способы, но они далеко не так просты, как хотелось бы. Если вы действительно хотите писать функции обработки текста с использованием рекурсивных шаблонов, будь моим гостем.

Основная слабость XSLT - обработка текста, что кажется разумным, поскольку его цель - визуализировать XML. Однако, поскольку содержимое XML полностью текстовое, XSLT требует более строгой обработки текста. Излишне говорить, что разработчикам таблиц стилей время от времени требуется некоторая расширяемость. С Xalan такую ​​расширяемость обеспечивает Java.

Используйте классы JDK в XSLT

Возможно, вам будет приятно узнать, что вам не нужно писать какой-либо код Java, чтобы воспользоваться преимуществами расширяемости Xalan. Когда вы используете Xalan, вы можете создавать и вызывать методы практически для любого объекта Java. Перед использованием класса Java вы должны предоставить для него пространство имен XSLT . В этом примере объявляется "java"пространство имен для всего в пакете Java или под ним (т. Е. Всего JDK):


  

Теперь нам нужно чем-то заняться. Начнем с небольшого XML-документа:

 Ява может быть причудой Дж. Берк 30.11.97  

Вас попросили оформить этот XML так, чтобы заголовок отображался в верхнем регистре. Разработчик, не знакомый с XSLT, просто откроет ссылку XSLT для поиска toUpper()функции; однако она будет разочарована, обнаружив, что в справочнике его нет. translate()Метод является лучшим выбором, но у меня есть еще лучший метод: java.lang.String.toUpperCase(). Чтобы использовать этот метод, вам необходимо создать экземпляр Stringобъекта с содержимым заголовка. Вот как вы можете создать новый Stringэкземпляр с содержимым элемента заголовка:


  

nameАтрибут задает дескриптор вашего нового Stringэкземпляра. Вы вызываете конструктор, сначала указывая пространство имен вместе с оставшимся путем к Stringклассу. Как вы могли заметить, Stringне хватает new()метода. Вы используете new()для создания объекта Java в Xalan; он соответствует newключевому слову Java . Аргументы, данные для new()определения версии конструктора, который будет вызван. Теперь, когда у вас есть содержимое заголовка в Stringобъекте Java , вы можете использовать этот toUpperCase()метод, например:


  

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

Ниже вы найдете еще один трюк. Вот как вы можете указать дату и время в любом месте таблицы стилей, используя java.lang.Date:


  

Вот что-то, что сделает день любым, кто потребует локализовать общую таблицу стилей между двумя или более языками. Вы можете использовать java.util.ResourceBundleдля локализации буквального текста в таблице стилей. Поскольку в вашем XML есть тег автора, вы можете указать его "Author:"рядом с именем человека.

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

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


  

ResourceBundleКласс ожидает найти файл с именем General.propertiesв вашем CLASSPATH. После создания пакета его можно повторно использовать во всей таблице стилей. В этом примере извлекается authorресурс:


  

Обратите внимание на странную сигнатуру метода. Обычно ResourceBundle.getString()принимает только один аргумент; однако в XSLT вам также необходимо указать объект, с помощью которого вы хотите вызвать метод.

Напишите свои собственные расширения

В некоторых редких ситуациях вам может потребоваться написать собственное расширение XSLT в форме функции расширения или элемента расширения. Я расскажу о создании функции расширения - концепции, которую довольно легко понять. Любая функция расширения Xalan может принимать строки в качестве входных и возвращать строки процессору XSLT. Ваши расширения также могут принимать NodeLists или Nodes в качестве аргументов и возвращать эти типы процессору XSLT. Использование Nodes или NodeLists означает, что вы можете добавить к исходному XML-документу функцию расширения, что мы и сделаем.

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

Пятница, 30 ноября 200 г.

Может ли стандартный XSLT завершить указанную выше дату? XSLT может выполнить большую часть задачи. Определить настоящий день - сложная часть. Один из способов быстро решить эту проблему - использовать java.text.SimpleDateкласс формата в функции расширения для возврата строки, отформатированной по нашему желанию. Но подождите: обратите внимание, что день выделен жирным шрифтом. Это возвращает нас к исходной проблеме. Причина, по которой мы даже рассматриваем функцию расширения, заключается в том, что в исходном XML-документе не удалось структурировать дату как группу узлов. Если наша функция расширения возвращает строку, нам все равно будет трудно изменить стиль поля дня, отличный от остальной части строки даты. Вот более полезный формат, по крайней мере, с точки зрения дизайнера XSLT:

  11 30 2001  

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

  30 ноября пятница 2001  

Класс, в котором находится наша функция расширения, ничего не реализует и не расширяет; мы назовем класс DateFormatter:

открытый класс DateFormatter {общедоступный статический формат узла (строковая дата) {} 

Вау, слишком просто, да? Абсолютно никаких требований к типу или интерфейсу функции расширения Xalan не предъявляется. Как правило, большинство функций расширения принимают Stringв качестве аргумента и возвращают другой String. Другими распространенными шаблонами являются отправка или получение org.w3c.dom.NodeLists или отдельных Nodes от функции расширения, как мы это сделаем. См. Документацию Xalan для получения подробной информации о том, как типы Java преобразуются в типы XSLT.

Во фрагменте кода выше format()логика метода разбивается на две части. Во-первых, нам нужно проанализировать строку даты из исходного XML-документа. Затем мы используем некоторые методы программирования DOM, чтобы создать Nodeи вернуть его процессору XSLT. Тело реализации нашего format()метода гласит:

 Document doc = DocumentBuilderFactory.newInstance(). newDocumentBuilder().newDocument(); Element dateNode = doc.createElement("formatted-date"); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(DateFormat.SHORT, locale); df.setLenient(true); Date d = df.parse(date); df.applyPattern("MMMM"); addChild(dateNode, "month", df.format(d)); df.applyPattern("EEEE"); addChild(dateNode, "day-of-week", df.format(d)); df.applyPattern("yyyy"); dateNode.setAttribute("year", df.format(d)); return dateNode; 

dateNode will contain our formatted date values that we return to the stylesheet. Notice that we've utilized java.text.SimpleDateFormat() to parse the date. This allows us to take full advantage of Java's date support, including its localization features. SimpleDateFormat handles the numeric date conversion and returns month and day names that match the locale of the VM running our application.

Remember: the primary purpose of an extension function is simply to allow us access to existing Java functionality; write as little code as possible. An extension function, like any Java method, can use other methods within the same class. To simplify the format() implementation, I moved repetitive code into a small utility method:

private void addChild (Node parent, String name, String text) { Element child = parent.getOwnerDocument().createElement(name); child.appendChild(parent.getOwnerDocument().createTextNode(text)); parent.appendChild(child); } 

Use DateFormatter within a stylesheet

Now that we have implemented an extension function, we can call it from within a stylesheet. Just as before, we need to declare a namespace for our extension function:


  

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

Чтобы использовать функцию, просто вызовите ее из selectтега, например: