• Keine Ergebnisse gefunden

Das Transformations-API in Java

Im Dokument Serielle Transformationen von XML (Seite 152-158)

7   STX-Integration   137

7.2 Das Transformations-API in Java

Java-Anwendungen, die STX-basierte Transformationen ausführen wollen, benötigen dazu eine standardisierte Schnittstelle (ein API), die unabhängig von der konkreten STX-Implementierung funktioniert. Auf diese Weise kann leicht die Implementierung

Serielle Transformationen von XML. Probleme, Methoden, Lösungen.

140 7  STX-Integration

der STX-Komponente durch eine andere ersetzt werden. Abhängigkeiten von einem bestimmten Produkt oder einem bestimmten Hersteller lassen sich vermeiden.

Für XSLT existiert in Java ein solches API. Es wurde ursprünglich unter dem Namen TrAX (Transformation API for XML) entwickelt und ist seit Version 1.1 (Februar 2001) Bestandteil von JAXP (Java API for XML Processing).

Unter den Designzielen für TrAX findet sich eine größtmögliche Unabhängigkeit von der konkreten Art der XML-Quelle und der Art des XML-Ergebnisses. Dazu werden die Interfaces Source und Result im Paket javax.xml.transform definiert. In JAXP selbst sind konkrete Klassen für Byteströme (StreamSource, StreamResult), SAX-Eventströme (SAXSource, SAXResult) sowie DOM-Bäume (DOMSource, DOMResult) enthalten. Andere APIs für die XML-Verarbei-tung können eigene Ausprägungen bereitstellen, wie beispielsweise JDOM mit den Klassen JDOMSource und JDOMResult[JDOM].

In der Ankündigung von TrAX auf der XSLT-Mailingliste findet sich darüber hinaus der Satz »While this interface is modeled on the XSLT process, it should be generic enough to use with many types of transformations besides XSLT.«4 Mangels Alterna-tiven gab es in der Realität jedoch bisher ausschließlich XSLT-basierte Implementa-tionen der TrAX-Schnittstellen.

Tatsächlich werden in diesem API keine Details der Transformationssprache sichtbar.

Es ist z.B. nicht möglich, auf XSLT-Anweisungen oder XSLT-Variablen zuzugreifen.

Ebenso existiert in TrAX keine Java-Repräsentation für XPath-Ausdrücke etc.

Sämtliche Interna der Transformation bleiben verborgen. So gesehen entspricht das Transformationsobjekt Transformer einer »Black Box«, deren interne Funktions-weise nicht zugänglich ist. Über TrAX kann auch jede andere Transformationssprache gekapselt werden.

Überblick Die in TrAX enthaltenen Funktionen ermöglichen

die Erzeugung von Transformationsobjekten, deren Transformationsanweisungen (etwa XSLT) aus einer XML-Quelle (Source) stammen,

das Setzen von Ausgabeeigenschaften für die Transformation, die Übergabe von externen Parametern an die Transformation und schließlich die Ausführung der Transformation.

Listing 43 zeigt diese Schritte anhand eines minimalen Beispiels.

Listing 43 Ein einfacher Aufruf einer Transformation in TrAX

1 import javax.xml.transform.*;

2 import javax.xml.transform.stream.*;

3

4 public class TraxExample

5 {

6 public static void main(String[] args)

7 {

8 String sourceId = args[0]; // die XML-Quelle

9 String stylesheet = args[1]; // das XSLT-Stylesheet

4übersetzt: »Obwohl diese Schnittstelle basierend auf XSLT modelliert wurde, sollte sie generisch genug sein, um mit vielen Transformationsarten neben XSLT benutzt zu werden.«

Siehe http://www.biglist.com/lists/xsl-list/archives/200004/msg01138.html

Dissertation, Oliver Becker, 1. Juli 2004

7.2  Das Transformations-API in Java 141

10

11 try {

12 // Factory erzeugen

13 TransformerFactory factory = TransformerFactory.newInstance();

14

15 // Transformer-Objekt erzeugen

16 Transformer transformer =

17 factory.newTransformer(new StreamSource(stylesheet));

18

19 // Ausführung der Transformation,

20 // Ausgabe des Ergebnisses auf die Konsole

21 transformer.transform(new StreamSource(sourceId),

Der abgedruckte Code implementiert im wesentlichen ein Kommandozeilentool für XSLT. Der erste Kommandozeilenparameter (args[0]) enthält den Namen der zu transformierenden XML-Datei, der zweite Kommandozeilenparameter (args[1]) den Namen des Stylesheet, das die Transformationsanweisungen beinhaltet.

Innerhalb des try-Blockes werden dann die drei zentralen Schritte bei der Verwen-dung von TrAX ausgeführt: es wird eine Factory für Transformer-Objekte angelegt (Zeile 13), diese produziert anschließend unter Angabe der zu verwendenden Trans-formationsanweisungen ein geeignetes Transformer-Objekt (Zeile 17), welches schließlich die Transformation ausführt (Zeile 21).

Ausgabe-eigenschaften und Parameter

Möchte man für die Transformation spezielle Ausgabeeigenschaften setzen oder ex-terne Parameter an sie übergeben, so stellt das erhaltene Transformer-Objekt dafür spezielle Methoden zur Verfügung, siehe Listing 44.

Listing 44 Ausgabeeigenschaften und Parameterübergabe in TrAX

1 import java.util.*;

2 import javax.xml.transform.*;

3 import javax.xml.transform.stream.*;

4

5 public class TraxExample

6 {

7 public static void main(String[] args)

8 {

9 // Anlegen des transformer-Objekts wie bekannt ...

10

11 // Ausgabeeigenschaften setzen (bzw. ändern)

12 Properties oprops = new Properties();

13 oprops.put(OutputKeys.ENCODING, "iso-8859-1");

14 transformer.setOutputProperties(oprops);

15

16 // Wert für einen externen Parameter übergeben

17 transformer.setParameter("language", "en");

18

19 // Ausführung der Transformation wie bekannt ...

Serielle Transformationen von XML. Probleme, Methoden, Lösungen.

142 7  STX-Integration

20 }

21 }

Hier wird die gewünschte Kodierung iso-8859-1 als Ausgabeeigenschaft gesetzt (Zeile 13) sowie die Zeichenkette "en" für den Parameter language übergeben (Zeile 17). Dies ist natürlich nur dann sinnvoll, wenn das verwendete Stylesheet eine Parameterdefinition der Form <xsl:param name="language" /> besitzt.

Für alle in XSLT 1.0 im Element xsl:output definierten Ausgabeeigenschaften existiert in der Klasse OutputKeys eine Konstante, deren Wert eine Zeichenkette ist. In obigem Beispiel hätte anstelle der Konstanten OutputKeys.ENCODING ebenso der Wert "encoding" angegeben werden können. Die Benutzung der Konstanten hat jedoch den Vorteil, dass eventuelle Schreibfehler bereits vom Compiler festgestellt werden.

Geparste Stylesheets Neben der hier beispielhaft angegebenen Grundform für die Ausführung von

XML-Transformationen über TrAX existiert eine weitere Variante, in der zunächst aus den Transformationsanweisungen ein Templates-Objekt erzeugt wird. Dieses stellt die Laufzeitrepräsentation eines Stylesheet dar und ermöglicht die effiziente Erzeugung mehrerer Transformer-Objekte. Auf diese Weise kann derselbe Transformations-code mehrmals verwendet werden, ohne dass die dazugehörigen Anweisungen mehrfach eingelesen werden müssen.

SAX-Anbindung

Für SAX existiert eine spezielle Variante, die die Transformation innerhalb einer Verarbeitungskette erlaubt. Die dafür zur Verfügung stehende Klasse SAX-TransformerFactory ist ein Spezialfall der bekannten TransformerFacto-ry. Sie enthält unter anderem die zusätzliche Methode newXMLFilter, die ein Transformationsobjekt in Form eines SAX-XMLFilters zurückgibt.

An dieser Stelle fällt jedoch eine Unzulänglichkeit in JAXP 1.1 auf: einer auf diese Weise ausgeführten Transformation lassen sich keine externen Parameter übergeben.

Stylesheet-Parameter in XSLT (xsl:param) lassen sich somit nicht benutzen.

Transformer-Handler JAXP definiert stattdessen für die Erstellung von SAX-Filterketten ein neues Interface

namens TransformerHandler. Dieses ist von den SAX-Interfaces Content-Handler und LexicalHandler abgeleitet und lässt sich intuitiver einsetzen als ein XMLFilter. Über die zusätzliche Methode setResult können Instanzen verschiedener Result-Ergebnistypen bei einem TransformerHandler ange-meldet werden.

Listing 45 demonstriert die Transformation eines SAX-Datenstroms in einer Pipeline mit zwei beteiligten XSLT-Stylesheets.

Listing 45 Verwendung von TransformerHandler

1 TransformerFactory tFactory = TransformerFactory.newInstance();

2 if (tFactory.getFeature(SAXTransformerFactory.FEATURE)) {

3 // Ok, SAX-Transformationen werden von dieser Factory unterstützt

4 SAXTransformerFactory saxTFactory = (SAXTransformerFactory) tFactory;

5 // erzeuge zwei (XSLT) Transformer-Objekte

6 TransformerHandler tHandler1 =

7 saxTFactory.newTransformerHandler(new StreamSource("step1.xsl"));

8 TransformerHandler tHandler2 =

9 saxTFactory.newTransformerHandler(new StreamSource("step2.xsl"));

Dissertation, Oliver Becker, 1. Juli 2004

7.2  Das Transformations-API in Java 143

10 XMLReader myReader = XMLReaderFactory.createXMLReader();

11 ContentHandler mySerializer = ...

12

13 // Aufbau einer Transformationskette

14 myReader.setContentHandler(tHandler1);

15 myReader.setProperty("http://xml.org/sax/properties/lexical-handler",

16 tHandler1);

17 tHandler1.setResult(new SAXResult(tHandler2));

18 tHandler2.setResult(new SAXResult(mySerializer));

19

20 // Start der Transformation

21 myReader.parse("input.xml");

22 }

23 else {

24 // SAX-basierte Transformation nicht möglich ...

25 }

Zunächst wird eine einfache TransformerFactory in eine SAXTransformer-Factory umgewandelt, nachdem über die getFeature-Methode (Zeile 2) sicher-gestellt wurde, dass diese Factory SAX-basierte Transformationen unterstützt.

Anschließend erzeugt die Factory zwei TransformerHandler-Objekte, die die Transformationsanweisungen aus der jeweils angegebenen Quelle (hier step1.xsl und step2.xsl) benutzen. Das erste dieser Objekte (tHandler1) wird dabei wie ein normaler SAX-Konsument eingesetzt (siehe Zeilen 14 bis 16). Das zweite Objekt (tHandler2) wird danach über die Methode setResult mit der ersten Transfor-mation verbunden, sodass eine zweistufige TransforTransfor-mationskette entsteht.

Stylesheet-Parameter müssen über einen kleinen Umweg an eine solche Transforma-tion übergeben werden. Ein TransformerHandler liefert über die Methode getTransformer ein passendes Transformer-Objekt, an dem die Methode setParameter aufgerufen werden kann.

Auch wenn die konstruierte Transformationskette einen kontinuierlichen Fluss von SAX-Events nahe legt, geschieht doch die Transformation im Falle von XSLT schubweise: tHandler1 konsumiert das gesamte Dokument, bevor das Ergebnis der Transformation in Form von SAX-Events an tHandler2 übergeben wird.

Dieser wartet wiederum das Ende des Eventstromes ab, bevor mit der zweiten Transformation begonnen wird. XSLT-Transformationen in einem SAX-Eventfluss wirken wie Staustufen. Allerdings wurde auch bereits darauf hingewiesen, dass TrAX keineswegs XSLT-spezifisch ist.

TrAX und STX

Wenn TrAX also so generisch entworfen wurde, dass es auch die Benutzung vieler anderer Arten von Transformationen ermöglicht, woher »wissen« die beteiligten Objekte dann, dass es sich um XSLT handelt? Die Antwort darauf lautet: Das ist implementationsabhängig.

Das in TrAX angewendete Factory Pattern (siehe [GHJ+95]) ermöglicht den Aus-tausch der verwendeten konkreten Klassen, ohne dass dazu der Quelltext umgeschrie-ben werden muss. Die Klasse TransformerFactory ist abstrakt; sie kann nicht direkt instantiiert werden. Stattdessen liefert der Aufruf

TransformerFactory factory = TransformerFactory.newInstance();

Serielle Transformationen von XML. Probleme, Methoden, Lösungen.

144 7  STX-Integration

ein Objekt vom Typ TransformerFactory. Von welcher konkreten abgeleiteten Klasse dieses Objekt eine Instanz ist, kann in Java über spezielle Properties eingestellt werden. Ohne diese zusätzliche Einstellung erhält man in Java 1.4 immer ein Factory-Objekt, das XSLT-Prozessoren »produziert«.

Das System-Property für den Namen der zu verwendenden Implementationsklasse heißt

"javax.xml.transform.TransformerFactory"

Um TrAX für eine andere Transformationssprache zu benutzen, muss man also nur für dieses Property eine Klasse angeben, die Transformationsobjekte für diese andere Sprache produziert.5

Die STX-Implementierung Joost unterstützt die TrAX-Interfaces. Der Klassenname für die TransformerFactory lautet in diesem Fall

"net.sf.joost.trax.TransformerFactoryImpl"

System-Properties können während der Programmausführung dynamisch gesetzt werden. Der folgende, in Listing 46 dargestellte Mechanismus entscheidet anhand des Namens der Transformationsdatei, ob eine XSLT- oder eine STX-Transformation durchgeführt werden soll.

Listing 46 Dynamische Auswahl der Transformations-Factory

1 if (stylesheet.endsWith(".stx"))

2 System.setProperty("javax.xml.transform.TransformerFactory",

3 "net.sf.joost.trax.TransformerFactoryImpl");

4 else

5 System.setProperty("javax.xml.transform.TransformerFactory",

6 "com.icl.saxon.TransformerFactoryImpl");

7

8 TransformerFactory factory = TransformerFactory.newInstance();

9 Transformer transformer =

10 factory.newTransformer(new StreamSource(stylesheet));

11 // usw ...

Die Zeichenkette "com.icl.saxon.TransformerFactoryImpl" in Zeile 6 verweist auf den XSLT-Prozessor Saxon.6 Leider sieht TrAX derzeit keine Mög-lichkeit vor, die gewünschte Transformationssprache zu spezifizieren, ohne eine konkrete Implementierung anzugeben. Sinnvoll wäre eine Zwischenstufe, auf der ein Nutzer des TrAX-API angeben kann, dass beispielsweise eine STX-Transformation ausgeführt werden soll, die konkrete Implementationsklasse jedoch plattformabhängig über geeignete Properties zur Laufzeit ausgewählt wird.

Wie bereits erwähnt, enthält die Klasse OutputKeys geeignete Konstanten für alle in XSLT 1.0 definierten Ausgabeeigenschaften. Sie ist damit XSLT-spezifisch. Dies schränkt jedoch die Unabhängigkeit des API nicht ein, da der Zugriff auf diese Eigen-schaften über die Standardklasse java.util.Properties erfolgt. Jede Eigen-schaft wird als Zeichenkette angesprochen. Andere Transformationssprachen können

5Es ist sogar denkbar, eine generische Factory zu implementieren, die erst beim Aufruf der Methode new-Transformer anhand der vorliegenden Transformationsanweisungen erkennt, um welche Sprache es sich handelt.

Ein gutes Erkennungsmerkmal wäre beispielsweise der Namensraum, in dem sich das Dokumentelement befindet.

6Genauer: auf die Version von Saxon, die XSLT 1.0 implementiert (derzeit 6.5.3). Ab Version 7 implementiert Saxon den kommenden Standard XSLT 2.0 und verwendet auch einen anderen Klassennamen.

Dissertation, Oliver Becker, 1. Juli 2004

7.2  Das Transformations-API in Java 145

ohne weiteres abweichende Ausgabeeigenschaften verwenden. Das Vorhandensein entsprechender Konstanten in OutputKeys bedeutet darüber hinaus nicht, dass jede Implementation diese Werte unterstützen muss. Unbekannte Schlüsselwerte können mit der Ausnahme IllegalArgumentException abgewiesen werden.

An dieser Stelle konnten nicht alle Möglichkeiten von TrAX erschöpfend behandelt werden. Eine ausführliche Darstellung über TrAX im Kontext von STX gibt Zubow in [Zub02].

Resümee

Ziel eines API für STX war zunächst das Bestreben, eine standardisierte Schnittstelle für STX-basierte Transformationen zu entwerfen. Ein solches API existiert für XSLT bereits unter dem Namen TrAX. Es ist ebenso für STX geeignet. Damit ergeben sich sogar weitere Vorteile:

Existierende Applikationen, die über die TrAX-Schnittstellen XSLT-basierte Transformationen ausgeführt haben, können mit minimalem Aufwand ebenfalls STX-basierte Transformationen benutzen.

Es kann dynamisch zwischen verschiedenen Transformationsarten umgeschaltet werden.

Unter anderem wurde Joost von den Apache-Entwicklern ohne großen Aufwand in das Publishing Framework Cocoon [ASFd] eingebunden. Dies demonstriert eindrucks-voll die Praxisrelevanz des gewählten TrAX-Ansatzes.

Im Dokument Serielle Transformationen von XML (Seite 152-158)