XSLT цъфти с Java

Случвало ли ви се е да бъдете затруднени от труден проблем с трансформацията на XML, който не сте могли да разрешите само с XSLT (Разширяема езикова трансформация на стилове)? Вземете например един прост лист със стилове на филтъра, който избира само тези възли, датирани преди по-рано от пет дни. Чували сте, че XSLT може да филтрира XML документи, така че смятате, че ще разрешите този проблем за нула време. Първата задача е получаването на днешната дата от таблица със стилове, при условие че информацията не е включена в оригиналния XML документ. За съжаление не можете да изпълните тази задача само с XSLT. В ситуация като тази можете да опростите своя XSLT код и да разрешите проблема по-бързо с разширение Java.

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

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

XSLT е важен за разработчиците на J2EE (Java 2 Platform, Enterprise Edition), защото стилизирането на XML документи се превърна в операция от страна на сървъра. Също така JAXP (Java API за обработка на 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 документ:

 Java може да бъде прищявка J. Burke 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()определяне на версията на конструктора, която ще бъде извикана. Сега, след като имате съдържанието на заглавието в Java Stringобект, можете да използвате toUpperCase()метода по следния начин:


  

Отначало това може да ви изглежда странно. Когато използвате Java методи на определен екземпляр, първият аргумент е екземплярът, за който искате да се извика методът. Очевидно Xalan използва самоанализ, за ​​да осигури тази възможност.

По-долу ще намерите друг трик. Ето как можете да излъчвате датата и часа навсякъде в таблицата със стилове, като използвате java.lang.Date:


  

Ето нещо, което ще направи деня на всеки, който трябва да локализира обща таблица със стилове между два или повече езика. Можете да използвате java.util.ResourceBundleза локализиране на буквален текст в таблица със стилове. Тъй като вашият XML има авторски маркер, може да искате да отпечатате "Author:"до името на човека.

Единият вариант е да се създаде отделна таблица със стилове за всеки локал, т.е. една за английски, друга за китайска и т.н. Проблемите, присъщи на този подход, трябва да са очевидни. Поддържането на последователност на няколко версии на таблици със стилове отнема време. Също така трябва да модифицирате приложението си, така че то да избере правилната таблица със стилове въз основа на локала на потребителя.

Вместо да дублирате таблицата със стилове за всеки език, можете да се възползвате от функциите за локализация на Java. Локализирането с помощта на 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. Обикновено повечето функции за разширение ще приемат a Stringкато аргумент и връщат друг String. Други често срещани модели са да изпращаме или получаваме org.w3c.dom.NodeLists или отделни Nodes от функция за удължаване, както ще направим и ние. Вижте документацията на Xalan за подробности за това как Java типовете се преобразуват в XSLT типове.

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

Документ на документ = DocumentBuilderFactory.newInstance (). newDocumentBuilder (). newDocument (); Елемент dateNode = doc.createElement ("форматирана дата"); SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance (DateFormat.SHORT, локал); df.setLenient (вярно); Дата d = df.parse (дата); df.applyPattern ("MMMM"); addChild (dateNode, "месец", df.format (d)); df.applyPattern ("EEEE"); addChild (dateNode, "ден от седмицата", df.format (d)); df.applyPattern ("гггг"); dateNode.setAttribute ("година", df.format (d)); return dateNode;

dateNodeще съдържа форматираните ни стойности за дата, които връщаме в таблицата със стилове. Забележете, че сме използвали java.text.SimpleDateFormat()за анализ на датата. Това ни позволява да се възползваме напълно от поддръжката на датата на Java, включително нейните функции за локализация. SimpleDateFormatобработва преобразуването на числови дати и връща имена на месеци и дни, които съответстват на локала на виртуалната машина, изпълняваща нашето приложение.

Запомнете: основната цел на функцията за разширение е просто да ни позволи достъп до съществуващата функционалност на Java; напишете възможно най-малко код. Функция за разширение, като всеки метод на Java, може да използва други методи в същия клас. За да опростя format()реализацията, преместих повтарящия се код в малък полезен метод:

private void addChild (родител на възел, име на низ, текст на низа) {Елемент дете = родител.getOwnerDocument (). createElement (име); child.appendChild (parent.getOwnerDocument (). createTextNode (текст)); parent.appendChild (дете); }

Използвайте DateFormatter в таблица със стилове

След като внедрихме функция за разширение, можем да я извикаме от таблица със стилове. Точно както преди, трябва да декларираме пространство от имена за нашата функция за разширение:


  

Този път напълно квалифицирахме пътя към класа, хостващ функцията за разширение. Това не е задължително и зависи от това дали ще използвате други класове в рамките на същия пакет или само един обект на разширение. Можете да декларирате пълното CLASSPATHкато пространство на имената или да използвате пакет и да посочите класа, където се извиква функцията за разширение. Като посочваме пълното CLASSPATH, въвеждаме по-малко, когато извикаме функцията.

За да използвате функцията, просто я извикайте от selectетикет, по следния начин: