XSLT blüht mit Java

Wurden Sie jemals von einem schwierigen XML-Transformationsproblem überrascht, das Sie mit XSLT (Extensible Stylesheet Language Transformation) allein nicht lösen konnten? Nehmen Sie zum Beispiel ein einfaches Filter-Stylesheet, das nur die Knoten auswählt, die vor fünf Tagen datiert wurden. Sie haben gehört, dass XSLT XML-Dokumente filtern kann, sodass Sie dieses Problem in kürzester Zeit lösen können. Die erste Aufgabe besteht darin, das heutige Datum in einem Stylesheet abzurufen, sofern die Informationen nicht im ursprünglichen XML-Dokument enthalten sind. Leider können Sie diese Aufgabe nicht nur mit XSLT ausführen. In einer solchen Situation können Sie Ihren XSLT-Code vereinfachen und das Problem mit einer Java-Erweiterung schneller lösen.

Viele XSLT-Prozessoren ermöglichen eine Art Erweiterungsmechanismus. Die Spezifikation verlangt dies von ihnen. In der Welt von Java und XML ist der am weitesten verbreitete XSLT-Prozessor der Open-Source-Apache-Xalan-Prozessor. Xalan wurde in Java geschrieben und ermöglicht Erweiterungen in Java. Viele Entwickler finden die Erweiterbarkeit von Xalan leistungsstark, da sie ihre Java-Kenntnisse im Stylesheet-Kontext nutzen können. Berücksichtigen Sie, wie JSPs (JavaServer Pages), Scriptlets und benutzerdefinierte Tags HTML mehr Leistung verleihen. Xalan-Erweiterungen erweitern Stylesheets auf die gleiche Weise: Sie ermöglichen Java-Entwicklern den Zugriff auf ihr Lieblingswerkzeug Java.

In diesem Artikel werde ich zeigen, wie Sie Java in einem XSLT-Stylesheet verwenden können. Zunächst verwenden wir die Erweiterbarkeit von Xalan, um vorhandene Klassen im JDK zu instanziieren und zu verwenden. Später werde ich Ihnen zeigen, wie Sie eine XSLT-Erweiterungsfunktion schreiben, die ein StringArgument verwendet und ein DOM-Fragment (Document Object Model) an den Stylesheet-Prozessor zurückgibt.

XSLT ist wichtig für J2EE-Entwickler (Java 2 Platform, Enterprise Edition), da das Gestalten von XML-Dokumenten zu einer serverseitigen Operation geworden ist. Außerdem ist JAXP (die Java-API für die XML-Verarbeitung), die Unterstützung für XSLT-Engines enthält, Teil der J2EE-Spezifikation (J2EE 2.6.11). In den Kinderschuhen sollte XSLT XML auf dem Client formatieren. Die meisten Anwendungen formatieren das XML jedoch, bevor es an den Client gesendet wird. Für J2EE-Entwickler bedeutet dies, dass der XSLT-Prozessor höchstwahrscheinlich auf dem App-Server ausgeführt wird.

Bevor Sie mit diesem Artikel fortfahren, sollten Sie gewarnt werden, dass die Verwendung von Java-Erweiterungen in Ihren XSLT-Stylesheets deren Portabilität verringert. Erweiterungen sind zwar Teil der XSLT-Spezifikation, ihre Implementierung jedoch nicht. Wenn Ihre Stylesheets auf anderen Prozessoren als Xalan ausgeführt werden, z. B. der Stylesheet-Engine von Internet Explorer, sollten Sie die Verwendung von Erweiterungen unter allen Umständen vermeiden.

XSLT-Schwächen

Da XSLT einige Schwachstellen aufweist, erweisen sich XSLT-Erweiterungen als sehr nützlich. Ich sage nicht, dass XSLT schlecht ist; Es bietet jedoch nicht das beste Tool, um alles in einem XML-Dokument zu verarbeiten. Betrachten Sie diesen Abschnitt von XML:

 XSLT ist nicht so einfach zu bedienen, wie manche es gerne hätten ...   

Angenommen, Ihr Chef fordert Sie auf, ein Stylesheet so zu ändern, dass alle Instanzen von "nicht" in "nicht" konvertiert werden und allgemeine Beschriftungen lokalisiert werden. Sicherlich bietet XSLT einen Mechanismus, um etwas in diese Richtung zu tun, oder? Falsch. XSLT bietet keine einfache Möglichkeit, das Auftreten eines Wortes oder Musters innerhalb einer Zeichenfolge zu ersetzen. Gleiches gilt für die Lokalisierung. Das heißt nicht, dass dies mit der Standard-XSLT-Syntax nicht möglich ist. Es gibt Wege, aber sie sind bei weitem nicht so einfach, wie wir es gerne hätten. Wenn Sie wirklich Textbearbeitungsfunktionen mit rekursiven Vorlagen schreiben möchten, seien Sie mein Gast.

Die Hauptschwäche von XSLT ist die Textverarbeitung, die vernünftig erscheint, da sie zum Rendern von XML dient. Da XML-Inhalt jedoch ausschließlich aus Text besteht, muss XSLT stärker mit Text behandelt werden. Es ist unnötig zu erwähnen, dass Stylesheet-Designer von Zeit zu Zeit eine gewisse Erweiterbarkeit benötigen. Mit Xalan bietet Java diese Erweiterbarkeit.

Verwenden Sie JDK-Klassen in XSLT

Es könnte Sie freuen zu wissen, dass Sie keinen Java-Code schreiben müssen, um die Erweiterbarkeit von Xalan zu nutzen. Wenn Sie Xalan verwenden, können Sie Methoden für fast jedes Java-Objekt erstellen und aufrufen. Bevor Sie eine Java-Klasse verwenden, müssen Sie einen XSLT- Namespace dafür bereitstellen . In diesem Beispiel wird "java"als Namespace für alles im oder unter dem Java-Paket (dh für das gesamte JDK) deklariert :


  

Jetzt müssen wir etwas tun. Beginnen wir mit einem kleinen XML-Dokument:

 Java kann eine Modeerscheinung sein J. Burke 30.11.97  

Sie wurden aufgefordert, dieses XML so zu gestalten, dass der Titel in Großbuchstaben angezeigt wird. Ein Entwickler, der neu in XSLT ist, öffnet einfach eine XSLT-Referenz, um nach der toUpper()Funktion zu suchen . Sie wäre jedoch enttäuscht, wenn sie feststellen würde, dass der Referenz eine fehlt. Die translate()Methode ist die beste Wahl, aber ich habe eine noch bessere Methode : java.lang.String.toUpperCase(). Um diese Methode zu verwenden, müssen Sie ein StringObjekt mit dem Titelinhalt instanziieren . So können Sie eine neue StringInstanz mit dem Inhalt des Titelelements erstellen :


  

Das nameAttribut gibt das Handle für Ihre neue StringInstanz an. Sie rufen den Konstruktor auf, indem Sie zuerst den Namespace zusammen mit dem verbleibenden Pfad zur StringKlasse angeben. Wie Sie vielleicht bemerkt haben, Stringfehlt eine new()Methode. Sie verwenden new(), um ein Java-Objekt in Xalan zu erstellen. es entspricht dem newSchlüsselwort von Java . Die Argumente zur new()Bestimmung der Konstruktorversion, die aufgerufen wird. Nachdem Sie den Titelinhalt in einem Java- StringObjekt haben, können Sie die toUpperCase()Methode folgendermaßen verwenden:


  

Das könnte für Sie zunächst seltsam aussehen. Wenn Sie Java-Methoden für eine bestimmte Instanz verwenden, ist das erste Argument die Instanz, für die die Methode aufgerufen werden soll. Offensichtlich verwendet Xalan Introspektion, um diese Fähigkeit bereitzustellen.

Unten finden Sie einen weiteren Trick. So können Sie Datum und Uhrzeit an einer beliebigen Stelle in Ihrem Stylesheet ausgeben java.lang.Date:


  

Hier ist etwas, das jeden zum Tag macht, der ein generisches Stylesheet zwischen zwei oder mehr Sprachen lokalisieren muss. Sie können java.util.ResourceBundleLiteraltext in einem Stylesheet lokalisieren. Da Ihr XML ein Autoren-Tag hat, möchten Sie möglicherweise "Author:"neben dem Namen der Person drucken .

Eine Möglichkeit besteht darin, für jedes Gebietsschema ein separates Stylesheet zu erstellen, dh eines für Englisch, eines für Chinesisch usw. Die mit diesem Ansatz verbundenen Probleme sollten offensichtlich sein. Es ist zeitaufwändig, mehrere Stylesheet-Versionen konsistent zu halten. Sie müssen Ihre Anwendung auch so ändern, dass sie das richtige Stylesheet basierend auf dem Gebietsschema des Benutzers auswählt.

Anstatt das Stylesheet für jede Sprache zu duplizieren, können Sie die Lokalisierungsfunktionen von Java nutzen. Die Lokalisierung mit Hilfe von a ResourceBundleerweist sich als besserer Ansatz. Laden Sie in XSLT Folgendes ResourceBundleam Anfang Ihrer Stylesheets wie folgt:


  

Die ResourceBundleKlasse erwartet, eine Datei zu finden, die General.propertiesin Ihrer aufgerufen wird CLASSPATH. Sobald das Bundle erstellt wurde, kann es im gesamten Stylesheet wiederverwendet werden. In diesem Beispiel wird die authorRessource abgerufen :


  

Beachten Sie noch einmal die seltsame Methodensignatur. Normalerweise ResourceBundle.getString()nimmt nur ein Argument; In XSLT müssen Sie jedoch auch das Objekt angeben, mit dem Sie die Methode aufrufen möchten.

Schreiben Sie Ihre eigenen Erweiterungen

In einigen seltenen Situationen müssen Sie möglicherweise Ihre eigene XSLT-Erweiterung in Form einer Erweiterungsfunktion oder eines Erweiterungselements schreiben. Ich werde das Erstellen einer Erweiterungsfunktion diskutieren, ein Konzept, das ziemlich einfach zu verstehen ist. Jede Xalan-Erweiterungsfunktion kann Zeichenfolgen als Eingabe verwenden und Zeichenfolgen an den XSLT-Prozessor zurückgeben. Ihre Erweiterungen können auch NodeLists oder Nodes als Argumente verwenden und diese Typen an den XSLT-Prozessor zurückgeben. Wenn Sie Nodes oder NodeLists verwenden, können Sie dem ursprünglichen XML-Dokument eine Erweiterungsfunktion hinzufügen. Dies werden wir tun.

Eine Art von Textelement, auf das häufig gestoßen wird, ist ein Datum. Es bietet eine großartige Gelegenheit für eine neue XSLT-Erweiterung. Unsere Aufgabe ist es, ein Artikelelement so zu gestalten, dass das Datum im folgenden Format gedruckt wird:

Freitag, 30. November 200

Can standard XSLT complete the date above? XSLT can finish most of the task. Determining the actual day is the difficult part. One way to quickly solve that problem is to use the java.text.SimpleDate format class within an extension function to return a string formatted as we wish. But wait: notice that the day appears in bold text. This returns us to the initial problem. The reason we are even considering an extension function is because the original XML document failed to structure the date as a group of nodes. If our extension function returns a string, we will still find it difficult to style the day field differently than the rest of the date string. Here's a more useful format, at least from the perspective of an XSLT designer:

  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:


  

Dieses Mal haben wir den Pfad zu der Klasse, in der sich die Erweiterungsfunktion befindet, vollständig qualifiziert. Dies ist optional und hängt davon ab, ob Sie andere Klassen innerhalb desselben Pakets oder nur ein einzelnes Erweiterungsobjekt verwenden. Sie können den vollständigen CLASSPATHWert als Namespace deklarieren oder ein Paket verwenden und die Klasse angeben, in der die Erweiterungsfunktion aufgerufen wird. Durch Angabe des vollständigen Wertes geben CLASSPATHwir weniger ein, wenn wir die Funktion aufrufen.

Um die Funktion zu nutzen, rufen Sie sie einfach aus einem selectTag heraus auf: