Javascript erlebt derzeit dank der AJAX-Technologie so etwas wie eine Wiederbelebung. Dieser Artikel beschreibt, wie man in Java einen Allzweck-XML-Prozessor schreibt und diesen dann mithilfe von ECMA-Script auf einen bestimmten XML-Dialekt zuschneidet.
Im letzten Monat ging es um die nützliche XMLBeans-Bibliothek[1], welche die Arbeit mit XML in Java um einiges erleichtert. Manchmal möchte man seine Anwendungen noch um Scripting-Funktionen erweitern, aber XML ist eine der am kniffligsten auszudrückenden Dinge in Scripting-Sprachen. Häufig landet man dann am Ende doch bei den zugrunde liegenden Java XML APIs. Doch diese können einem nicht immer die "reichhaltigen, dynamischen Ausdrucksmöglichkeiten" bieten, wie man sie eigentlich von seiner Scriptsprache erwartet.
Genau an diesem Punkt kommt E4X ins Spiel, ECMA-Script for XML, eine von BEA entwickelte Erweiterung der Javascript-Sprache (in der formalen standardisierten Form als ECMA-Script bekannt). Javascript erlebt derzeit so etwas wie eine Renaissance dank der AJAX-Technologie, welche auf Javascript zum Hinzufügen dynamischer Elemente zu Webseiten zurückgreift. E4X erweitert ECMA-Script, indem es XML zu einem nativen Typ innerhalb der Sprache macht, wodurch man damit so einfach arbeiten kann, wie mit anderen nativen Typen.
Noch wichtiger aber ist, dass E4X-Funktionen bereits in der Javascript-Engine von Mozilla (Rhino)[2] implementiert sind, und zwar mithilfe der XMLBeans-Bibliothek. Rhino wiederum ist Bestandteil der Referenzimplementierung von JSR 223 (Scripting for Java Platform), welche wiederum Bestandteil von Java 6 ist (Codename "Mustang"), das sich derzeit in der Entwicklungsphase befindet (Neugierige können schon einmal einen Blick auf die Development Snapshots[3] werfen). Hier soll ein Blick auf ein paar Schnipsel von E4X-Code im Rhino Javascript-Interpreter geworfen werden. Wer die Beispiele selber ausprobieren will, muss sich Rhino sowie die XMLBeans-Bibliotheken herunterladen. Dann gibt man den folgenden Befehl auf der Kommandozeile ein:
Dabei fügt man die entsprechenden Pfade für XMLBeans und Rhino ein und startet damit den Javascript-Interpreter in einem interaktiven Modus samt E4X-Unterstützung, wie der "js>"-Prompt zeigt. Damit kann man seine ersten Gehversuche mit XML unternehmen:
Dies erzeugt eine XML-Variable "a". Man beachte, dass man bei dieser Zuweisung direkt XML verwenden kann.
Wenn man jetzt auf ein oder mehrere bestimmte Elemente zugreifen will, kann man dies über deren Namen machen:
Wenn ein Element mehrfach vorkommt, bekommt man alle Elemente zurückgegeben:
Aber man kann auf sie immer noch mit einem Array zugreifen:
Um auf die Attribute zuzugreifen, verwendet man ein @ vor dem Attributnamen:
Man kann das XML-Dokument aber nicht nur passiv betrachten, sondern Elementen und Attributen auch Werte zuweisen.
Bei der ersten Zuweisung wird das erste firstname-Element durch ein neues Element ersetzt. Man hätte auch einfach den Wert "ECMA" zuweisen können und dieselbe Ausgabe erhalten, aber dabei wäre die Attributinformation verloren gegangen, da der String nur in den angegebenen Elementnamen eingefügt worden wäre, mit dem String als Wert. Die nächste Zuweisung setzt einfach das id-Attribut des name-Elements. Die dritte Zuweisung ergänzt das erste firstname-Element um das Attribut "official" und setzt dieses auf "yes". Bei E4X wird ein noch nicht existierendes Attribut einfach neu erstellt. Dasselbe gilt für Elemente: Wenn man einem nichtexistenten Element einen Wert zuweist, wird dieses Element erzeugt. So kann man Listen auch Elemente hinzufügen:
Und natürlich kann man die Elemente auch alle in einer Schleife bearbeiten:
a..firstname wählt firstname-Elemente, wo immer sie im Dokument auftauchen, so wie der "//"-Operator in XPath.
Damit erhält man einen kleinen Vorgeschmack, was man mit E4X alles anstellen kann und es ist schon zu erkennen, welche Möglichkeiten für die XML-Verarbeitung einem damit beim Scripting offen stehen.Als Nächstes geht es um eine Beispielanwendung, bei der XMLBeans und E4X zusammenspielen.
Die alles entscheidende Frage lautet: "Was gibt es Neues im Fernsehen?" Hierzu kann man auf das Fernsehprogramm von Bleb.org zurückgreifen, welches im XML-Format vorliegt, und nach neuen Serien Ausschau halten. Allerdings hat Bleb.org verständlicherweise strenge Regeln, was das Anzapfen ihrer XML-Listen betrifft. Man sollte diese höchstens alle zwei Sekunden abrufen. Die hier vorgestellte Anwendung soll das Herumexperimentieren mit Bleb-Listen ermöglichen, aber auf die Einhaltung der Spielregeln achten. Hier kommt das TV-Digger-Beispiel.
Die Anwendung besteht aus drei Elementen: der DigTV-Klasse, welche verantwortlich ist für die Integration der Rhino/Javascript-Engine, der GetTV-Klasse, welche für das Abrufen der XML-Dokumente von den entfernten Webseiten sorgt, und schließlich der Scriptdatei digscript.js. Zuerst ein Blick auf dieses Script, besonders auf die Funktion processUpdate:
Diese Funktion übernimmt ein Dokument als Parameter. Hier sollte dies ein String oder ein XMLObject sein. In jedem Fall bereitet die Funktion schedule=XML(document) es für die Verarbeitung per E4X vor.
Die nächste Zeile hält nach neuen Fernsehserien Ausschau, wobei auf die Funktionen von E4X zum Stringvergleich zurückgegriffen wird. "schedule..programme" bedeutet, dass alle programme-Elemente ausgewählt werden. Danach folgt ein Filter für diese Auswahl. Der Ausdruck muss wahr sein, falls das programme-Element ein desc-Element enthält, auf das der reguläre Ausdruck zutrifft, oder falls es ein subtitle-Element enthält, auf das derselbe reguläre Ausdruck zutrifft. Der Rückgabewert ist ein Array aller passenden programme-Elemente.
Da damit aber auch die enthaltenen Elementinformationen zu Sender und Datum verloren gegangen sind, muss man den zurückgegebenen Programmen entsprechende Attribute hinzufügen.
Damit sind die Angaben vollständig und man kann sie einer vorhandenen XML-Variable hinzufügen.
Der Zuordnungsoperator "+=" bewirkt, dass das Element am Ende der Liste angefügt wird. Falls es noch kein programme-Element gibt, wird das Fragment einfach an das finaldoc-Dokument angefügt. Gibt es bereits programme-Elemente, wird es nach dem letzten angefügt.
Das ist das Kernstück des Scripts. Die Variable finaldoc wird global wie folgt definiert:
Es gibt eine reportNow-Funktion, welche das Dokument einfach ausgibt, und eine getChannels-Funktion, welche ein Array aller Sendernamen zur Weiterverarbeitung zurückgibt. Wie man sieht, gibt es im ganzen Script nur Funktionen. Um den Aufruf des Scripts kümmert sich die DigTV-Klasse.Normalerweise hätte man hierfür das Bean Scripting Framework (BSF) verwendet, aber die Entwicklung von BSF und Rhino verläuft nicht mehr ganz synchron: Das BSF-Projekt hat zwar im CVS Änderungen vorgenommen, aber noch kein offizielles Release herausgebracht, welches diese Änderungen enthält. Ältere Versionen von Rhino funktionieren damit zwar, nicht aber die E4X-fähigen, daher wird hier zum Aufrufen der Rhino Javascript-Engine auf Rhinos eigene Embedding-Funktionen zurückgegriffen.
DigTVs Konstrukter lädt die Scriptdatei und wertet sie aus. Dann sucht er die im Script definierten Funktionen.
Ein Context enthält die Thread-spezifischen Informationen für Rhino. Über die Funktion enter wird der aktuelle Thread mit einem Kontext verknüpft. Der Geltungsbereich (scope) ist ein scriptfähiges Objekt, welches alle Javascript-Funktionen und -Variablen enthält. Ein solcher Geltungsbereich ist unabhängig vom Kontext, der ihn erstellt hat. initStandardObjects liefert einen Geltungsbereich, indem die Standard-Javascript-Objekte definiert sind. Nun kann man ein Script auswerten.
Damit wird das Script gelesen. Man erinnere sich: Das Script besteht vor allem aus Funktionsdefinitionen, auf die man wie folgt zugreifen kann:
Die Methode getFunction ruft die get-Methode von scope auf, was die Abfrage von jedem benannten Objekt im ausgewerteten Javascript ermöglicht, und überprüft, ob auch ein Function-Objekt zurückgegeben wird und nicht zum Beispiel eine Variable. Sobald dies alles erledigt ist, muss man den erstellten Kontext wieder verlassen, um die Verknüpfung mit dem Thread zu lösen.
In der GetTV-Klasse wird ein Timer verwendet um den Code aufzurufen, so dass eine XML-Datei von einer erzeugten URL abgerufen und geparst wird, was ein XMLBeans-XMLObject liefert. Dann wird die process-Methode von DigTV aufgerufen:
Der Kontext wird betreten und die call-Methode der Javascript-Funktion aufgerufen, wobei das XMLObject als Parameter übergeben wird. Wie man sich erinnert, wird das XMLObject in der Datei digscript.js verpackt und verarbeitet. Sobald dies erledigt ist, wird der Kontext wieder verlassen. So einfach ist das, es gilt jedoch ein paar Fallstricke zu beachten. Derzeit ist es noch schwierig, die zugrunde liegenden XMLObjects aus E4X-XML-Objekten zu extrahieren, daher sollte man derzeit die Schnittstelle für XMLObjects noch als Einbahnstraße von Java zu Javascript betrachten.
Sobald der GetTV-Timer seine Aufgabe erledigt hat, ruft er die Funktion reportNow auf, um anzuzeigen, dass er fertig ist, und macht dann für 24 Stunden Pause.
Wenn man den Wert von finaldoc ausgibt, erhält man Folgendes:
Man erstellt eine XML-Variable namens doc, mit einem leeren HTML-Element.
Dann erzeugt man ein Header-Element, welches das title-Element mit dem Wert "New Series" enthält. Dank der Kurzschreibweise von E4X ist dies ein einfacher Ausdruck, denn Elemente, auf die man sich bezieht, die aber noch nicht existieren, werden kurzerhand erzeugt.
Dann iteriert man über die vorher erstellte Liste der programme-Elemente:
Dies ist noch ein Beispiel für die erwähnte Kurzschreibweise. Diesmal wird eine Liste von tr-Elementen erweitert, die sich innerhalb eines table-Elements innerhalb eines body-Elements befindet, die alle wie benötigt erzeugt werden. Der Wert des anzuhängenden Elements wird als literales XML angegeben, allerdings unter Ausnutzung von E4Xs Fähigkeit zur Inline-Evaluation, bei der ein Ausdruck innerhalb geschweifter Klammern {} ausgewertet wird. Mithilfe der Methode .text() erhält man nur den Text eines Elements. Hier muss man genau sein, denn wenn es zulässig ist, liefert ein Ausdruck das ganze XML-Fragment zurück statt nur den Wert. Beim href-Attribut kann man darauf verzichten, denn hier ist nur ein einziger Wert möglich.
Wenn man diesen Code auf das oben gezeigte XML-Beispiel anwendet, erhält man folgende Ausgabe:
Was man mit diesem HTML-Code anfängt, bleibt dem Leser überlassen. Aber man sollte einen Einblick bekommen haben, wie man Java, E4X und XMLBeans in seinen eigenen Anwendungen nutzen und Java, Scripting und XML einfach verknüpfen kann. Dabei bleibt E4X nicht auf die Java-VM beschränkt, sondern wird inzwischen auch von Browsern wie Firefox unterstützt.
URLs in diesem Artikel:
[1] = http:/
[2] = http:/
[3] = https:/