XSLT kwitnie wraz z Javą

Czy kiedykolwiek zdumiałeś się trudnym problemem związanym z transformacją XML, którego nie możesz rozwiązać za pomocą samego XSLT (Extensible Stylesheet Language Transformation)? Weźmy na przykład prosty arkusz stylów filtru, który wybiera tylko te węzły datowane wcześniej niż pięć dni temu. Słyszałeś, że XSLT może filtrować dokumenty XML, więc myślisz, że rozwiążesz ten problem w mgnieniu oka. Pierwszym zadaniem jest pobranie dzisiejszej daty z arkusza stylów, pod warunkiem, że informacje nie są zawarte w oryginalnym dokumencie XML. Niestety, nie możesz wykonać tego zadania, używając tylko XSLT. W takiej sytuacji możesz uprościć kod XSLT i szybciej rozwiązać problem za pomocą rozszerzenia Java.

Wiele procesorów XSLT dopuszcza pewien rodzaj mechanizmu rozszerzeń; specyfikacja tego wymaga. W świecie języków Java i XML najczęściej używanym procesorem XSLT jest procesor Apache Xalan typu open source. Napisany w Javie, Xalan pozwala na rozszerzenia w Javie. Wielu programistów uważa, że ​​rozszerzalność Xalana jest potężna, ponieważ pozwala im wykorzystać ich umiejętności w języku Java w kontekście arkusza stylów. Zastanów się, w jaki sposób strony JSP (JavaServer Pages), skryptlety i niestandardowe znaczniki dodają mocy do HTML. Rozszerzenia Xalan dodają moc do arkuszy stylów w podobny sposób: umożliwiając programistom Java dostęp do ich ulubionego narzędzia, Javy.

W tym artykule pokażę, jak można używać języka Java w arkuszu stylów XSLT. Po pierwsze, użyjemy rozszerzalności Xalana do tworzenia instancji i używania istniejących klas w JDK. Później pokażę ci, jak napisać funkcję rozszerzenia XSLT, która pobiera Stringargument i zwraca fragment DOM (Document Object Model) do procesora arkusza stylów.

XSLT jest ważny dla programistów J2EE (Java 2 Platform, Enterprise Edition), ponieważ stylizowanie dokumentów XML stało się operacją po stronie serwera. Ponadto JAXP (Java API for XML Processing), który obejmuje obsługę silników XSLT, stał się częścią specyfikacji J2EE (J2EE 2.6.11). W początkowej fazie XSLT miał nadawać styl XML na kliencie; jednak większość aplikacji stylizuje XML przed wysłaniem go do klienta. W przypadku deweloperów J2EE oznacza to, że procesor XSLT najprawdopodobniej będzie działał na serwerze aplikacji.

Przed kontynuowaniem tego artykułu ostrzegamy, że używanie rozszerzeń Java w arkuszach stylów XSLT zmniejszy ich przenośność. Chociaż rozszerzenia są częścią specyfikacji XSLT, sposób ich implementacji już nie. Jeśli Twoje arkusze stylów będą działały na procesorach innych niż Xalan, takich jak silnik arkuszy stylów przeglądarki Internet Explorer, powinieneś za wszelką cenę unikać używania rozszerzeń.

Słabości XSLT

Ponieważ XSLT ma kilka słabych punktów, rozszerzenia XSLT są całkiem przydatne. Nie mówię, że XSLT jest zły; jednak po prostu nie oferuje najlepszego narzędzia do przetwarzania wszystkiego w dokumencie XML. Rozważ tę sekcję XML:

 XSLT nie jest tak łatwy w użyciu, jak niektórzy chcieliby ...   

Załóżmy, że szef prosi cię o zmodyfikowanie arkusza stylów, tak aby konwertował wszystkie wystąpienia „nie jest” na „nie jest” i lokalizuje typowe etykiety. Z pewnością XSLT zapewnia mechanizm do zrobienia czegoś podobnego, prawda? Źle. XSLT nie zapewnia łatwego sposobu na zastąpienie wystąpienia słowa lub wzorca w ciągu. To samo dotyczy lokalizacji. Nie oznacza to, że nie można tego zrobić za pomocą standardowej składni XSLT. Są sposoby, ale nie są one tak łatwe, jak byśmy chcieli. Jeśli naprawdę chcesz pisać funkcje manipulacji tekstem przy użyciu szablonów rekurencyjnych, bądź moim gościem.

Główną słabością XSLT jest przetwarzanie tekstu, co wydaje się rozsądne, ponieważ jego celem jest renderowanie XML. Jednak ponieważ zawartość XML jest w całości tekstowa, XSLT wymaga silniejszej obsługi tekstu. Nie trzeba dodawać, że projektanci arkuszy stylów wymagają od czasu do czasu pewnej rozszerzalności. Dzięki Xalan Java zapewnia taką rozszerzalność.

Użyj klas JDK w XSLT

Możesz być zadowolony, wiedząc, że nie musisz pisać żadnego kodu Java, aby skorzystać z rozszerzalności Xalana. Używając Xalana, możesz tworzyć i wywoływać metody na prawie każdym obiekcie Java. Przed użyciem klasy Java należy zapewnić dla niej przestrzeń nazw XSLT . W tym przykładzie zadeklarowano "java"jako przestrzeń nazw dla wszystkiego w pakiecie Java lub pod nim (tj. Całego JDK):


  

Teraz musimy coś zrobić. Zacznijmy od małego dokumentu XML:

 Java May Be a Fad J. Burke 30.11.97  

Poproszono Cię o stylizowanie tego XML, aby tytuł był pisany wielkimi literami. Programista nowy w XSLT po prostu otworzyłby odwołanie XSLT w celu wyszukania toUpper()funkcji; byłaby jednak rozczarowana, gdyby okazało się, że odniesienie go nie ma. translate()Metoda jest najlepszy, ale mam jeszcze lepszą metodę: java.lang.String.toUpperCase(). Aby użyć tej metody, musisz utworzyć instancję Stringobiektu z zawartością tytułu. Oto jak możesz utworzyć nową Stringinstancję z zawartością elementu title:


  

nameAtrybut określa uchwyt do nowej Stringinstancji. Wywołujesz konstruktora, najpierw określając przestrzeń nazw wraz z pozostałą ścieżką do Stringklasy. Jak być może zauważyłeś, Stringbrakuje mu new()metody. Używasz new()do konstruowania obiektu Java w Xalan; odpowiada newsłowu kluczowemu Java . Argumenty podane w celu new()określenia wersji konstruktora, która zostanie wywołana. Teraz, gdy masz już zawartość tytułu w Stringobiekcie Java , możesz użyć tej toUpperCase()metody:


  

Na początku może to wyglądać dziwnie. W przypadku korzystania z metod Java w konkretnej instancji pierwszym argumentem jest instancja, na której ma zostać wywołana metoda. Oczywiście Xalan wykorzystuje introspekcję, aby zapewnić tę możliwość.

Poniżej znajdziesz kolejną sztuczkę. Oto jak możesz emitować datę i godzinę w dowolnym miejscu arkusza stylów, używając java.lang.Date:


  

Oto coś, co sprawi, że każdy dzień będzie musiał zlokalizować ogólny arkusz stylów między dwoma lub więcej językami. Możesz użyć java.util.ResourceBundledo zlokalizowania dosłownego tekstu w arkuszu stylów. Ponieważ twój XML ma znacznik autora, możesz chcieć wydrukować "Author:"obok nazwiska osoby.

Jedną z opcji jest utworzenie osobnego arkusza stylów dla każdego języka, tj. Jednego dla angielskiego, drugiego dla chińskiego i tak dalej. Problemy nieodłącznie związane z tym podejściem powinny być oczywiste. Utrzymywanie spójności wielu wersji arkuszy stylów jest czasochłonne. Musisz także zmodyfikować swoją aplikację, aby wybierała właściwy arkusz stylów na podstawie ustawień regionalnych użytkownika.

Zamiast powielać arkusz stylów dla każdego języka, możesz skorzystać z funkcji lokalizacji Java. Lokalizowanie za pomocą a ResourceBundleokazuje się lepszym podejściem. W XSLT załaduj ResourceBundlena początku swojego arkusza stylów, na przykład:


  

ResourceBundleKlasa spodziewa się znaleźć plik o nazwie General.propertiesw twojej CLASSPATH. Po utworzeniu pakietu można go ponownie wykorzystać w całym arkuszu stylów. Ten przykład pobiera authorzasób:


  

Zwróć uwagę ponownie na dziwną sygnaturę metody. Zwykle ResourceBundle.getString()przyjmuje tylko jeden argument; jednakże w XSLT musisz także określić obiekt, za pomocą którego chcesz wywołać metodę.

Napisz własne rozszerzenia

W niektórych rzadkich sytuacjach może być konieczne napisanie własnego rozszerzenia XSLT w postaci funkcji rozszerzającej lub elementu rozszerzającego. Omówię tworzenie funkcji rozszerzającej, koncepcję dość łatwą do zrozumienia. Każda funkcja rozszerzająca Xalan może przyjmować ciągi jako dane wejściowe i zwracać ciągi do procesora XSLT. Twoje rozszerzenia mogą również przyjmować NodeLists lub Nodes jako argumenty i zwracać te typy do procesora XSLT. Używanie Nodes lub NodeLists oznacza, że ​​możesz dodać do oryginalnego dokumentu XML funkcję rozszerzenia, co właśnie zrobimy.

Jednym z często napotykanych elementów tekstowych jest data; zapewnia doskonałą okazję do nowego rozszerzenia XSLT. Naszym zadaniem jest wystylizowanie elementu artykułu, aby data była drukowana w następującym formacie:

Piątek, 30 listopada 200

Czy standardowy XSLT może uzupełnić powyższą datę? XSLT może zakończyć większość zadania. Ustalenie rzeczywistego dnia jest trudną częścią. Jednym ze sposobów szybkiego rozwiązania tego problemu jest użycie java.text.SimpleDateklasy formatu w funkcji rozszerzającej w celu zwrócenia ciągu sformatowanego zgodnie z naszymi oczekiwaniami. Ale poczekaj: zwróć uwagę, że dzień jest pogrubiony. W ten sposób wracamy do pierwotnego problemu. Powodem, dla którego rozważamy nawet funkcję rozszerzenia, jest to, że oryginalny dokument XML nie zdołał uporządkować daty jako grupy węzłów. Jeśli nasza funkcja rozszerzająca zwraca ciąg, nadal będziemy mieć trudności ze stylizowaniem pola dnia inaczej niż w przypadku reszty ciągu daty. Oto bardziej przydatny format, przynajmniej z punktu widzenia projektanta XSLT:

  11 30 2001  

We now create an XSLT extension function, taking a string as an argument and returning an XML node in this format:

  November 30 Friday 2001  

The class hosting our extension function doesn't implement or extend anything; we will call the class DateFormatter:

public class DateFormatter { public static Node format (String date) {} 

Wow, too easy, huh? There are absolutely no requirements placed on the type or interface of a Xalan extension function. Generally, most extension functions will take a String as an argument and return another String. Other common patterns are to send or receive org.w3c.dom.NodeLists or individual Nodes from an extension function, as we will do. See the Xalan documentation for details on how Java types convert to XSLT types.

In the code fragment above, the format() method's logic breaks into two parts. First, we need to parse the date string from the original XML document. Then we use some DOM programming techniques to create a Node and return it to the XSLT processor. The body of our format() method implementation reads:

 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:


  

Tym razem w pełni kwalifikowaliśmy ścieżkę do klasy hostującej funkcję rozszerzającą. Jest to opcjonalne i zależy od tego, czy będziesz używać innych klas w tym samym pakiecie, czy tylko jednego obiektu rozszerzenia. Możesz zadeklarować full CLASSPATHjako przestrzeń nazw lub użyć pakietu i określić klasę, w której wywoływana jest funkcja rozszerzająca. Określając full CLASSPATH, podczas wywoływania funkcji wpisujemy mniej.

Aby skorzystać z tej funkcji, po prostu wywołaj ją z poziomu selecttagu, na przykład: