Sonntag, 12. Februar 2012





April 2006
aus XML & Web Services Magazin Ausgabe: 2.2004
Wahlerfolg
Generierung von SVG-Code mithilfe von XSLT
von Herbert Bader

Der XML-Vektorgrafikstandard ermöglicht unter anderem die Erzeugung dynamischer Webgrafiken und eignet sich daher gut für den Einsatz im Bereich Reporting. Bei der Generierung des eigentlichen SVG-Codes kann dabei unter anderem auf die XSLT, die eXtensible Stylesheet Language Transformations, zurückgegriffen werden.


Wenn Sie außerhalb rein didaktischer Zwecke mit SVG zu tun haben, werden Sie nur selten in einem Texteditor SVG-Tags verfassen. Dem Screendesigner stehen Tools zur Verfügung, um komplexe Seiten zu erstellen, dem Report-Kreator zusätzlich Programmiersysteme.


Ein typischer Werdegang eines Online-Reports in SVG wird folgendermaßen verlaufen:

  • Der Systemarchitekt legt zusammen mit der Fachseite die Menge der darzustellenden Geschäftsdaten fest und beschreibt die Aufbereitung (Model).
  • Ein Screendesigner kreiert das Layout nach fachseitiger Maßgabe, aber ohne Kenndaten. Die Objekte, die Kenndaten benötigen, werden mit Ersatzwerten als Platzhalter erstellt (View). Das Ergebnis ist ein Template (zu Deutsch: Schablone).
  • Programmierer sorgen dafür, dass zur Online-Zeit diese Platzhalter automatisch durch Geschäftsdaten - oder davon abgeleitete Größen - ersetzt werden (Controller).

Die Aufgabe der Programmierer kann dabei unterschiedlich tief in SVG eingreifen. Bei Balkengrafiken muss vermutlich nur die Höhe als einziger Parameter dynamisch ersetzt werden. Dagegen müssen bei einem Kuchendiagramm eine Reihe von (zum Teil trigonometrischen) Berechnungen angestellt werden und es sind etliche Objekte betroffen.


Auch die Wahl der serverseitigen Programmiersprache hängt von der Komplexität dieses Ersetzungsprozesses ab. Bei einfachen Grafiken genügt die XSLT.

XSL-Transformation
Die prädestinierte Sprache zur Wandlung eines XML-Formats in ein anderes sind die eXtensible Stylesheet Language-Transformations, kurz die XSLT. In einer XSL-Datei werden Regeln definiert, wie Tags und Elemente des einen Formats in das andere Format umgebaut werden müssen. Diese Regeln werden ihrerseits in einem XML-Format festgelegt; das heißt, auch XSL ist ein Substandard von XML.


Allerdings erlaubt diese deklarative Sprache nur einfache mathematische Operationen, wenn man keine Zusatzmodule einbinden möchte. Das Einbinden solcher zusätzlichen Erweiterungen ist für sich ein solcher Aufwand, dass man in den meisten Fällen besser von vornherein eine funktionale Programmiersprache wählt. Deshalb lassen wir diesen Fall außen vor.

Operationen
Die Operationen, die mit XSLT möglich sind, sind:
  • die Grundrechenarten,
  • logische Abfragen,
  • Schleifen.

Trigonometrische Funktionen, Wurzeln oder dergleichen sind im Sprachumfang von XSL nicht vorgesehen. Gegenüber dem Nachteil der eingeschränkten Funktionalität wartet XSLT bei der Codegenerierung mit einem großen Vorteil auf: Das Model-View-Controller-Konzept ist hierbei in Reinform umgesetzt. Die einzelnen Komponenten
  • xml-Datei = Model
  • xsl-Datei = View
  • Transformator = Controller

sind deutlich von einander getrennt, was zu einer guten Strukturierung der Gesamtanwendung führt.
Im Laufe dieses Abschnitts erfahren Sie, wie Sie die Möglichkeiten von XSLT dazu nut-zen können, einfache Grafiken wie Abbildung 1 (absolute Wählerstimmen mit Gewinnen und Verlusten) aus einer allgemeinen XML-Datei transformieren können. Daten in XML sind oft das Ergebnis eines vorgelagerten Prozesses und liegen meist mit beschreibenden Element- und Attributsnamen vor (im Unterschied zu (X)HTML, wo die Metadaten nur gestalterischen, aber keinen informativen Charakter haben).


Abb. 1: Positive und negative Werte in einer Balkengrafik

Die darzustellenden Werte
Für eine XSL-Transformation brauchen wir die darzustellenden Werte auf jeden Fall in XML-Form. Dies ist jedoch keine wirkliche Einschränkung mehr, denn moderne Tabellenkalkulationsprogramme und Datenbanken geben Abfrageergebnisse optional im XML-Format aus. Dabei wird typischerweise jedes Datenfeld zum Text eines Textelements (oder Attributwerts). Die Tag-Namen entsprechen sinnigerweise meist den Spaltennamen. Im konkreten Beispiel sehe die XML-Ausgangsdatei aus wie in Listing 1gezeigt.



Listing 1

<wahlergebnis jahr="2002" typ="Zweitstimmen_der_Bundestagswahl">
<partei id="SPD"> <farbe>#D60027</farbe> <prozente>38.5</prozente> <trend>-2.4</trend> </partei>
<partei id="CDU/CSU"> <farbe>#00003F</farbe> <prozente>38.5</prozente> <trend> 3.3</trend> </partei>
<partei id="Grüne"><farbe>#339900</farbe> <prozente>8.6</prozente> <trend> 1.9</trend> </partei>
<partei id="FDP"> <farbe>#fff000</farbe> <prozente>7.4</prozente> <trend> 1.1</trend> </partei>
<partei id="PDS"> <farbe>#ff0000</farbe> <prozente>4.0</prozente> <trend>-1.1</trend> </partei>
<partei id="PRO"> <farbe>#2F3F5B</farbe> <prozente>0.8</prozente> <trend> 0.8</trend> </partei>
<partei id="sonstige"> <farbe>#999999</farbe> <prozente>2.2</prozente> <trend>-3.6</trend> </partei>
</wahlergebnis>


Ein XML-Parser braucht zusätzliche Informationen über die Strukturierung des Dokuments, die wir ihm über die DTD mitteilen:

<!DOCTYPE wahlergebnis [
ELEMENT farbe (#PCDATA)
ELEMENT prozente (#PCDATA)
ELEMENT trend (#PCDATA)
ELEMENT partei (farbe, prozente, trend)
]>

Die DTD kann im XML-Dokument selbst oder als externe Datei darin referenziert sein. Im Begleitquellocde heißt die Datei (inklusive DTD) wahl.xml.

Allgemeines zu Stylesheets
Durch die Vorgabe, die Transformationsregeln in XML zu formatieren, ist die Syntax etwas gewöhnungsbedürftig. (Auch XSL-Code ist letztlich nicht dazu gedacht, direkt editiert zu werden, sondern als Output eines grafischen Tools erzeugt zu werden.) An dieser Stelle gebe ich einige Hinweise, um Ihnen mithilfe von Sekundärliteratur [1] den Einstieg in die Materie zu erleichtern:
  • Der Transformator geht - als Parsererweiterung - elementweise voran. Er arbeitet die Elemente von außen nach innen und von oben nach unten hin schrittweise ab.
  • Dabei prüft er bei jedem Element, ob er es einer Behandlung unterziehen muss. Das sieht er in der Liste der Einträge in der XSL-Datei. Findet er dort keinen Eintrag, verwirft er das Element selbst und alle dessen untergeordnete Elemente.
  • Ein XSL-Element der Form ... zeigt dem Transformator an, dass er ein Element namens tag-name auf die in "..." beschriebene Weise prozessieren soll. Dort können unter anderem XML-konforme Konstrukte stehen, die das Ausgangs-Tag tag-name ersetzen.
  • Innerhalb eines xsl:template-Elements kann stehen. Es fordert den Transformator auf, an dieser Stelle die untergeordneten Elemente zu prozessieren.

Weitere XSL-Konstrukte werden im Rahmen der einzelnen Beispiele erläutert. Als Erstes transformieren wir die wahl.xml in eine html-Datei.

Einfache Transformation nach HTML
Da wir bei einer HTML-Tabellendarstellung ohne grafische Elemente auskommen müs-sen, hinterlegen wir die Felder wenigstens mit der in wahl.xml mitgelieferten Farbe. Das Listing der wahl2html.xsl wird jeweils unterbrochen für Kommentare.

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

deklariert das Root-Element einer jeden XSL-Datei mit Angabe des Namensraums xsl der Transformation,

<xsl:decimal-format decimal-separator="," grouping-separator="." />

die Anweisung zur Formatierung in deutsches Zahlenformat.
Es folgt die erste Template-Regel, die auf das Root-Element von wahl.xml wirkt. In den head-Bereich des XHTML-Ergebnisses wird ein Stück CSS-Code gepflanzt. Mit JavaScript-Code verliefe dies völlig analog (siehe Listing 2).



Listing 2

<xsl:template match="wahlergebnis">
<html>
<head>
<style type="text/css">
<xsl:comment>
td {
color: white;
font-weight: bold;
font-size: 16pt;
}
// </xsl:comment>
</style>
<title>Wahlergebnis in HTML</title>
</head>
<body bgcolor="#FFFFFF">
<table bgcolor="#DDDDDD" cellpadding="10" cellspacing="10">
<th><font size="+1">Partei</font></th>
<th>Prozente</th>
<th>geg. 1998</th>
<xsl:apply-templates />
</table>
</body>
</html>
</xsl:template>


So weit die Behandlung des Root-Elements. Es wird ersetzt durch den angegebenen HTML-Code. HTML-Kommentarzeichen müssen besonders bezeichnet werden, um nicht fehlinterpretiert zu werden.
Im Element partei wird der Text des Elements farbe über eine lokale Variable col einem Attribut als Wert zugewiesen. Der Zuweisungsoperator für den Variablenwert an ein Attribut ist "{$variablen_name}". Dagegen meint <xsl:apply-templates select="@id"/> den Attributswert von id im XML:

<xsl:template match="partei">
<xsl:variable name="col" select="farbe" />
<tr bgcolor="{$col}">
<td><font size="+1">
<xsl:apply-templates select="@id" /></font>
</td>
<xsl:apply-templates select="prozente|trend" />
</tr>
</xsl:template>

Die Wirkung der xsl:apply-templates wurde zweimal eingeschränkt. Das Erste wird durch den Attributwert von id ersetzt, das Zweite weist an, nur die Kind-Elemente prozente und trend (und nicht farbe) zu prozessieren.


Die Textknoten von beiden kommen jeweils in ein Tabellenfeld:

<xsl:template match="prozente">
<td align="right">
<xsl:value-of select="format-number(.,'#0,0')" />
</td>
</xsl:template>
<xsl:template match="trend">
<td align="right">
<xsl:value-of select="format-number(.,'+#0,0;-#0,0')" />
</td>
</xsl:template>

Die Formatierungsanweisungen geben an, den Text (".") als Zahl zu interpretieren und mit deutschem Komma und (unten) immer mit Vorzeichen auszugeben.

</xsl:stylesheet>

Die Transformation ergibt eine einfache HTML-Tabelle (siehe wahlxslt.html) mit farbig hinterlegten Parteinamen.


Nach dieser Trainingseinheit sind wir fit für den Ernstfall.

Transformation nach SVG
Es folgt das Listing der XSL-Datei zur Erzeugung der Abbildung 1. Die ersten Zeilen von wahl2svg.xsl unterscheiden sich nicht von der HTML-Version:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl=
"http://www.w3.org/1999/XSL/Transform</font>">
<xsl:decimal-format decimal-separator="," grouping-separator="." />

Das Root-Element transformieren wir in den obligatorischen svg-Rahmen. Das Format teilen wir durch einen ersten waagerechten Strich in zwei übereinander liegende Hälften auf:

<xsl:template match="wahlergebnis">
<svg viewBox="0 0 1600 900">
<line x1="100" x2="1500" y1="450" y2="450" stroke-width="20"/>
<xsl:apply-templates select="partei" mode="absolut"/>
<line x1="100" x2="1500" y1="700" y2="700" stroke-width="20"/>
<xsl:apply-templates select="partei" mode="relativ"/>
</svg>
</xsl:template>

Die Definition der Behandlung des partei-Elements gibt es in zwei Modi. In den beiden Hälften kommen die beiden unterschiedlichen Behandlungen zum Tragen.


Es folgt der partei-Modus "absolut" für die obere Zeichnungshälfte:

<xsl:template match="partei" mode="absolut">
<g font-size="30" text-anchor="middle">
<rect x="{position()*200-50}" y="{450-prozente*10}"
width="100" height="{prozente*10}" fill="{farbe}" />
<text x="{position()*200}" y="{425-prozente*10}"><xsl:value-of <br></br> select="format-number(prozente, '#0,0')" /> %</text>
<text x="{position()*200}" y="500"><xsl:value-of
select="@id" /></text>
</g>
</xsl:template>

Neu sind in diesem Codeausschnitt die Berechungen der Koordinatenwerte. Der Wert eines untergeordneten Textknotens (wie farbe) wird einfach in geschweiften Klammern zu einem Attributswert. Bei der Wertzuweisung von height und y sehen Sie, dass mit diesem (automatisch in einen Zahlenwert transformierten) Wert auch gerechnet werden kann. Die XSLT-Systemfunktion position() schließlich gibt die fortlaufende Nummer des aktuellen Kontexts an (Schleifenparameter). Sie liefert die Antwort auf die Frage: Die wievielte partei bearbeiten wir gerade?


Wir kommen zur unteren Hälfte der Grafik, zur Erzeugung der Differenzwertbalken. Die Schwierigkeit ist dabei, dass wir entscheiden müssen, ob der Balken von oben zur Linie (positiver Wert) verläuft oder von der Linie nach unten (negativer Wert). Was für eine funktionale Sprache nicht schwer zu implementieren wäre, macht mit XSLT durchaus Mühe:

<xsl:template match="partei" mode="relativ">

Die Definition des relativ-Modus beginnt mit einer Reihe von Variablendefinitionen. Wir besetzen den Wert der Variable value mit dem Absolutwert von trend:

<xsl:param name="value"> 
<xsl:choose>
<xsl:when test="trend > 0">
<xsl:value-of select="trend"/></xsl:when>
<xsl:otherwise>
<xsl:value-of select="-trend"/></xsl:otherwise>
</xsl:choose>
</xsl:param>

Die Variable top ergibt die Oberkante (y-Wert). In die Berechnung eingesetzt wird $value als Wert der Variablen value:

<xsl:param name="top">
<xsl:choose>
<xsl:when test="trend > 0">
<xsl:value-of select="700-10*$value" /></xsl:when>
<xsl:otherwise>
<xsl:value-of select="700"/> </xsl:otherwise>
</xsl:choose>
</xsl:param>

Ohne Fallunterscheidung kommt die Belegung der Variablen valpos aus:

<xsl:param name="valpos">
<xsl:value-of select="$top -25" /></xsl:param>

Mit Verwendung dieser Definitionen gestaltet sich die eigentliche Berechnung der Balken ohne größere Überraschungen:

<g font-size="30" text-anchor="middle">
<rect x="{position()*200-50}" y="{$top}" width="100"
height="{10*$value}" fill="{farbe}" />
<text x="{position()*200}" y="{$valpos}"><xsl:value-of
select="format-number(trend, '+#0,0;-#0,0')" /> %</text>
</g>

Mehr gab es in diesem Stylesheet nicht zu transformieren.

</xsl:template>
</xsl:stylesheet>

Das Ergebnis des Transformationsprozesses sehen Sie in wahlxslt.svg.

Die Beispiele live
Statt die Dateien wahlxslt.html und wahlxslt.svg im fertigen Zustand im Begleitcode auf der CD anzusehen, können Sie sie auch selbst erzeugen. Alle benötigten Tools bekommen Sie kostenfrei unter den hier aufgelisteten Adressen.


Um die Transformationen selbst betreiben zu können, brauchen Sie einen XSLT-Prozes-sor. Die meisten, insbesondere die frei erhältlichen, sind in der Programmiersprache Java implementiert. Für deren Betrieb brauchen Sie zusätzlich eine Java Runtime-Umgebung (zum Beispiel ein JDK von Sun [2]).


Das gängigste Paket ist wohl Xalan [3] von der Apache Software Foundation. Im Folgenden beschreibe ich, wie Sie die Beispiele damit zum Laufen bekommen. Alternative Klassenpakete mit XSL-Transformatoren gibt es zum Beispiel bei Saxon [4] und Aztecrider [5]. Xalan wird in manchen JDK-Klassenbibliotheken bereits mit ausgeliefert - in diesem Fall brauchen Sie außer einem Java Development Kit nichts weiter.


Die aufzurufende Klasse ist org.apache.xalan.xslt.Process. Die Datei, die Sie transformieren wollen, geben Sie über den Parameter -in, das Transformations-Stylesheet über -xsl und das gewünschte Ergebnis über -out an. Die Syntax des Aufrufs lautet dann (bitte kein Zeilenumbruch vor ?):

prompt@konsole> java org.apache.xalan.xslt.Process  -in wahl.xml -xsl wahl2svg.xsl -out wahlxslt.svg ?

Wenn Ihre Java-Umgebung dagegen noch nicht über Xalan verfügt (Sie merken das an der Fehlermeldung NoClassDefFoundError), müssen Sie Xalan für Java von der genannten Adresse bei apache.org besorgen. Installieren Sie das System und merken Sie sich die Adresse des binary-Pakets xalan.jar. Der Aufruf erweitert sich dann um die Einbindung dieser Datei in den Klassenpfad:

prompt@konsole> java -cp /pfad/zu/xalan.jar org.apache.xalan.xslt. Process -in wahl.xml -xsl wahl2svg.xsl -out wahlxslt.svg ?

(auf Windows-Konsolen stattdessen: ... -cp \pfad\zu\xalan.jar ...)


Falls Sie ein älteres Paket xalan.jar benutzen, müssen Sie unter Umständen auch noch einen externen Parser inkludieren. Mit Xerces (ebenfalls bei apache.org erhältlich) erweitert sich die Klassenpfaddefinition zu:

-cp /pfad/zu/xalan.jar:/pfad/zu/xerces.jar

Falls Sie die Installationsmühen als übertrieben empfinden, um doch nur eine simple Konsolenanwendung zu Übungszwecken betreiben zu können, steht Ihnen dazu eine Alternative offen: die Web Publishing-Frameworks.

Web Publishing-Frameworks
Dies sind im Allgemeinen umfangreiche Softwarepakete mit erheblichem Installationsbedarf. Doch einmal installiert, fungieren sie als universelle Server für eine Vielzahl von Darstellungskanälen. Die gleichen Dateninhalte werden dabei in Online-Zeit - je nach Abruf vom Client - in HTML, WML, SVG, X3D, Java-GUI, Excel-Tabelle, PDF und viele andere Formate ausgegeben.


Auch für Web Publishing-Frameworks steht Ihnen eine vortreffliche Open-Source-Implementierung zur Verfügung. Es handelt sich dabei um das Projekt Cocoon [6].

Andere Beispiele in der Literatur
Nicht alle Autoren vertreten meinen Standpunkt, bei aufwändigeren Berechnungen besser auf eine funktionale Programmiersprache zu wechseln. Folgende beiden Literaturstellen zeigen Alternativen dazu auf:


Mit XSLT und SVG Diagramme für Geschäftsdokumente erzeugen [7]: Der Artikel von Jens Kleemann und Aidan Mark Humphreys beschreibt unter anderem einen Workaround über Taylorreihen, wie komplexe mathematische Funktionen auch mit den Grundrechenarten realisiert werden können. Auch zeigen die Autoren, dass solche Funktionalität in template-Abschnitten gekapselt und diese wie Funktionen verwendet werden können. Deshalb sprechen sie von XSLT in diesem Sinne folgerichtig als einer funktionalen Sprache.


J. David Eisenberg, SVG Essentials [8]: Eisenberg bringt ebenfalls ein aufwändiges Beispiel und erklärt die Grundzüge von XSLT sehr elementar. Er führt sein Beispiel so weit fort, bis die Berechnung eine komplexe mathematische Anforderung erreicht. Eisenbergs Lösung ist es, Java-Funktionen zu benut-zen, und er zeigt, wie externe Funktionalität in XSLT eingebunden wird.


Dieser Beitrag ist ein Auszug aus dem Buch SVG Reporting - Vektorgrafiken im web einsetzen von Herbert Bader, das im Januar 2004 im Software & Support Verlag erschienen ist (ISBN 3-935042-43-4).



Links und Literatur


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das Entwickler Magazin direkt über unser Online-Formular.

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen




Software & Support Verlag GmbH