Native Erweiterungen von Saxon und Xalan nutzen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie möchten wissen, wie Sie einige der nützlichen Erweiterungen in diesen verbreiteten XSLT-Implementierungen für sich einsetzen können.

Lösung

XSLT 1.0

Dieses Rezept ist in eine Reihe von Minirezepten aufgeteilt, in denen die wichtigsten Saxon- und Xalan-Erweiterungen demonstriert werden. In allen Beispielen ist das saxon-Namensraumpräfix mit "http://icl.com/saxon" bzw. das xalan-Namensraumpräfix mit "http://xml.apache.org/xslt" assoziiert.

Sie möchten mehr als eine Zieldatei ausgeben

In diesem Buch wurde schon mehrmals Saxons Fähigkeit benutzt, Ergebnisse in mehr als eine Datei auszugeben. Dazu verwendet Saxon das Element saxon:output. Es bietet zwar auch das xsl:document-Element, das aber nur dann funktioniert, wenn das version-Attribut des Stylesheets gleich 1.1 ist. Deswegen wird das hier nicht bevorzugt behandelt. Das href-Attribut gibt das Ausgabeziel an. Dieses Attribut kann auch ein Attributwert-Template sein:

<saxon:output href="toc.html">
  <html>
    <head><title>Table of Contents</title></head>
    <body>
      <xsl:apply-templates mode="toc" select="*"/>
    </body>
  </html>
</saxon:output>

Xalan verfolgt einen ganz anderen Ansatz für die Ausgabe in mehrere Zieldateien. Statt nur eine Instruktion gibt es in Xalan dafür gleich drei: redirect:open, redirect:close und redirect:write. Der mit diesen Elementen assoziierte Erweiterungsnamensraum lautet xmlns:redirect="org.apache. xalan.xslt.extensions.Redirect". In den meisten vorkommenden Fällen genügt es, lediglich redirect:write zu benutzen, weil es dann die Datei selbst öffnet, schreibt und schließt.

Jedes Element enthält ein Dateiattribut und/oder ein Auswahlattribut für die Angabe der Ausgabedatei. Das Dateiattribut ist ein String, d.h., Sie können darin den Namen einer Ausgabedatei direkt angeben. Das select-Attribut darf auch ein XPath-Ausdruck sein, d.h., dann können Sie den Namen einer Ausgabedatei dynamisch generieren. Falls Sie beide Attribute verwenden, dann wertet die redirect-Erweiterung zuerst das select-Attribut aus, und wenn dieser Ausdruck keinen gültigen Dateinamen ergibt, dann wertet sie das file-Attribut aus:

<xalan:write file="toc.html">
  <html>
    <head><title>Table of Contents</title></head>
    <body>
      <xsl:apply-templates mode="toc" select="*"/>
    </body>
  </html>
</xalan:write>

Wenn Sie die erweiterten Möglichkeiten von Xalan verwenden, können Sie beim Schreiben einer Hauptausgabedatei zu anderen zweitrangigen Dateien wechseln, während die Hauptdatei geöffnet bleibt. Dieses Vorgehen untergräbt die XSLT-Eigenschaft, frei von Seiteneffekten zu sein, aber Xalan sorgt dabei hoffentlich dafür, dass diese Operation ein vorhersehbares Verhalten hat:

<xsl:template match="doc">
  <xalan:open file="regular.xml"/>
  <xsl:apply-templates select="*"/>
  <xalan:close file="regular.xml"/>
<xsl:template/>
<xsl:template match="regular">
  <xalan:write file="regular.xml">
    <xsl:copy-of select="."/>
  </xalan:write/>
</xsl:template>
<xsl:template match="*">
  <xsl:variable name="file" select="concat(local-name( ),'.xml')"/>
  <xalan:write select="$file">
    <xsl:copy-of select="."/>
  </xalan:write/>
</xsl:template>

XSLT 2.0 unterstützt mehrere Ergebniszieldateien direkt mit Hilfe eines neuen Elements namens xsl:result-document:

<xsl:result-document format="html" href="toc.html">
  <html>
    <head><title>Table of Contents</title></head>
    <body>
      <xsl:apply-templates mode="toc" select="*"/>
    </body>
  </html>
</xsl:result-document>

Sie möchten eine komplexe Transformation in eine Reihe kleinerer Transformationen in einer Pipeline aufteilen

Entwickler, die viel mit Unix gearbeitet haben, sind besonders gut mit dem Konzept einer Verarbeitungspipeline vertraut, bei der die Ausgabe eines Befehls zur Eingabe eines anderen wird. Diese Einrichtung gibt es auch in anderen Betriebssystemen wie Windows. Das Geniale am Pipeline-Ansatz in der Softwareentwicklung ist, dass es dadurch möglich wird, komplexe Aufgaben aus einfacheren Befehlen zusammenzusetzen.

Da eine XSLT-Transformation letztendlich einen Baum in einen Baum transformiert, liegt es nahe, den Pipeline-Ansatz zu verwenden. Hierbei wird aus dem Ergebnisbaum einer Transformation der Eingabebaum einer weiteren Transformation. Sie haben bereits zahlreiche Beispiele gesehen, in denen die Funktion zur Erweiterung von Knotenmengen Zwischenergebnisse erzeugen kann, die in nachfolgenden Schritten verarbeitet werden können. Alternativ dazu enthält Saxon diese Funktionalität in dem Erweiterungsattribut saxon:next-in-chain von xsl:output. Das Attribut saxon:next-in-chain leitet die Ausgabe an ein anderes Stylesheet weiter. Sein Wert ist die URL eines Stylesheets, mit dem der Ausgabestrom bearbeitet werden soll. Der Ausgabestrom muss immer aus purem XML bestehen, und Attribute, die das Format der Ausgabe steuern (z.B. method, cdata-section-elements usw.) haben keinerlei Effekt. Die Ausgabe des zweiten Stylesheets wird an das Ziel weitergeleitet, das beim ersten Stylesheet benutzt worden wäre, wenn es kein saxon:next-in-chain Attribut gäbe.

Xalan verfolgt einen anderen Ansatz für diese Funktionalität: Es benutzt ein pipeDocument-Erweiterungselement. Das Schöne an pipeDocument ist, dass Sie es in einem sonst leeren Stylesheet benutzen können, um eine Pipeline zwischen unabhängigen Stylesheets zu erzeugen, die nicht wissen, dass sie auf diese Weise benutzt werden. Die Xalan-Implementierung entspricht daher viel mehr einer Unix-Pipe, weil die Pipeline in den beteiligten Stylesheets nicht fest kodiert ist. Stellen Sie sich vor, dass ein Stylesheet namens strip.xslt bestimmte Elemente aus einem XML-Dokument herausschneidet, das ein Buch darstellt, und ein Stylesheet namens contents.xslt, das eine Tabelle mit dem Inhaltsverzeichnis erzeugt, die auf der hierarchischen Struktur der Dokument-Markups basiert. Sie könnten auf folgende Weise eine Pipeline zwischen diesen Stylesheets erstellen:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pipe="xalan://PipeDocument" extension-element-prefixes="pipe">
  <xsl:param name="source"/>
  <xsl:param name="target"/>
  <!-- Eine Liste zu erhaltender Elemente. Alle anderen werden entfernt. -->
  <xsl:param name="preserve-elems"/>
  <pipe:pipeDocument source="{$source}" target="{$target}">
    <stylesheet href="strip.xslt">
      <param name="preserve-elems" value="{$preserve-elems}"/>
    </stylesheet>
    <stylesheet href="contents.xslt"/>
  </pipe:pipeDocument>
</xsl:stylesheet>

Dieser Code würde ein Inhaltsverzeichnis auf Basis der angegebenen Elemente erzeugen, ohne den davon unabhängigen Nutzen von strip.xsl oder contents.xsl zu beeinträchtigen.

Sie möchten mit Zeit- und Datumsangaben arbeiten

Unter Datums- und Zeitangaben haben Sie eine große Menge an Rezepten gesehen, die mit Zeit- und Datumsangaben zu tun hatten. Es gab aber keine Möglichkeit, allein mit XSLT die aktuelle Zeit und das aktuelle Datum zu bestimmen. Sowohl Saxon als auch Xalan implementieren Kernfunktionen aus dem EXSLT-Modul für Zeit- und Datumsangaben. Dieser Abschnitt enthält die entsprechende EXSLT-Dokumentation als schnellen Überblick über diese Funktionen. Sie finden diese Funktionen in der folgenden Tabelle zusammen mit deren Rückgabetyp, gefolgt vom Namen der Funktion und ihrer Argumente. Dabei steht ein Fragezeichen (?) für optionale Argumente.

Tabelle: Zeit- und Datumsfunktionen in EXSLT.

Funktion Verhalten
string date: date-time( ) Die Funktion date:date-time gibt das aktuelle Datum und die aktuelle Zeit als Datums-/Zeit-String zurück. Der zurückgegebene String muss in dem Format sein, das XML Schema als lexikalische Darstellung von xs:dateTime definiert.
string date: date(string?) Die Funktion date:date gibt das Datum im übergebenen Datums-/Zeit-String-Argument zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
string date: time(string?) Die Funktion date:time gibt die Zeit im übergebenen Datums-/Zeit-String-Argument zurück. Ohne Argument wird die aktuelle lokale Zeit als Vorgabewert benutzt, die von date:date-time zurückgegeben wird.
number date: year(string?) Die Funktion date:year gibt die Jahreszahl einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
boolean date: leap-year(string?) Die Funktion date:leap-year gibt true zurück, falls das im Datum angegebene Jahr ein Schaltjahr ist. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: month-in-year(string?) Die Funktion date:month-in-year gibt den Monat einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
string date: month-name(string?) Die Funktion date:month-name gibt den vollständigen Monatsnamen zu einer Datumsangabe zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
string date: month-abbreviation(string?) Die Funktion date:month-abbreviation gibt den abgekürzten Monatsnamen zu einer Datumsangabe zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: week-in-year(string?) Die Funktion date:week-in-year gibt die Woche im Jahr einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird. Die Zählweise erfolgt gemäß ISO 8601: Die erste (1) Woche eines Jahres ist diejenige, in die der erste Donnerstag des Jahres fällt, wobei eine Woche immer Montags anfängt.
number date: day-in-year(string?) Die Funktion date:day-in-year gibt den Tag im Jahr einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: day-in-month(string?) Die Funktion date:day-in-month gibt den Monatstag einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: day-of-week-in-month(string?) Die Funktion date:day-of-week-in-month gibt den Wochentag eines Monats als Zahl zurück (z.B. 3 für den dritten Dienstag im Mai). Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: day-in-week(string?) Die Funktion date:day-in-week gibt den Wochentag einer Datumsangabe als Zahl zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
string date: day-name(string?) Die Funktion date:day-name gibt den vollständigen Namen des Wochentages einer Datumsangabe zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
string date: day-abbreviation(string?) Die Funktion date:day-abbreviation gibt den abgekürzten Namen des Wochentages einer Datumsangabe zurück. Ohne Argument wird das aktuelle lokale Datum als Vorgabewert benutzt, das von date:date-time zurückgegeben wird.
number date: hour-in-day(string?) Die Funktion date:hour-in-day gibt die Stunde des Tages einer Zeitangabe als Zahl zurück. Ohne Argument wird die aktuelle lokale Zeit als Vorgabewert benutzt, die von date:date-time zurückgegeben wird.
number date: minute-in-hour(string?) Die Funktion date:minute-in-hour gibt die Minute in der Stunde einer Zeitangabe als Zahl zurück. Ohne Argument wird die aktuelle lokale Zeit als Vorgabewert benutzt, die von date:date-time zurückgegeben wird.
number date: second-in-minute(string?) Die Funktion date:second-in-minute gibt die Sekunde in der Minute einer Zeitangabe als Zahl zurück. Ohne Argument wird die aktuelle lokale Zeit als Vorgabewert benutzt, die von date:date-time zurückgegeben wird.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:date="http://exslt.org/dates-and-times">
  <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="/">
    <html>
      <head><title>My Dull Home Page</title></head>
      <body>
        <h1>My Dull Homepage</h1>
        <div>It's <xsl:value-of select="date:time( )"/> on <xsl:value-of select="date:date()"/> and this page is as dull as it was yesterday.</div>
      </body>
    </html> 
  </xsl:template>
</xsl:stylesheet>

In XSLT 2.0 gibt es eine eingebaute Unterstützung für Datums- und Zeitangaben, die unter Datums- und Zeitangaben erläutert werden. Das heißt, diese Erweiterungen sind dann nicht notwendig.

Sie brauchen eine effizientere Implementierung von Mengenoperationen

XML abfragen untersuchte verschiedene Möglichkeiten bei der Implementierung von Mengenoperationen, außer für Vereinigung von Mengen, die XPath mit dem Vereinigungsoperator (|) direkt unterstützt. Diese Lösungen waren nicht unbedingt die effizientesten oder am leichtesten zu verstehen.

Sowohl Saxon als auch Xalan beheben dieses Problem dadurch, dass sie die Mengenoperationen implementieren, die im set-Modul von EXSLT definiert werden (siehe folgende Tabelle).

Tabelle: EXSLT-Operationen auf Mengen.

Funktion Verhalten
Node-set set: difference(node-set, node-set) Die Funktion set:difference gibt die Differenz zwischen zwei Knotenmengen zurück, d.h. jene Knoten, die nur im ersten, aber nicht im zweiten übergebenen Knotenmengenargument enthalten sind.
Node-set set: intersection(node-set, node-set) Die Funktion set:intersection gibt die Schnittmenge der Knoten zurück, die in beiden übergebenen Knotenmengenargumenten enthalten sind.
Node-set set: distinct(node-set) Die Funktion set:distinct gibt eine Untermenge der Knoten zurück, die in der als Argument übergebenen Knotenmenge NS enthalten sind. Dabei wird ein Knoten N dann übernommen, falls kein anderer Knoten in NS den gleichen Stringwert wie N hat und in der Dokumentreihenfolge vor N steht.
Boolean set: has-same-node(node-set, node-set) Die Funktion set:has-same-node gibt true zurück, falls die im ersten Argument übergebene Knotenmenge Knoten enthält, die auch im zweiten Argument vorkommen. Falls keine Knoten in beiden Knotenmengen vorkommen, gibt sie false zurück.
Node-set set: leading(node-set, node-set) Die Funktion set:leading gibt die im ersten übergebenen Knotenmengenargument enthaltenen Knoten zurück, die in der Dokumentreihenfolge vor dem ersten Knoten des zweiten übergebenen Knotenmengenarguments stehen. Falls der erste Knoten in der zweiten Knotenmenge in der ersten Knotenmenge nicht vorkommt, wird eine leere Knotenmenge zurückgegeben. Falls die zweite Knotenmenge leer ist, dann wird die erste Knotenmenge zurückgegeben.
Node-set set: trailing(node-set, node-set) Die Funktion set:trailing gibt die im ersten übergebenen Knotenmengenargument enthaltenen Knoten zurück, die in der Dokumentreihenfolge dem ersten Knoten in der Knotenmenge folgen, die als zweites Argument übergeben wird. Falls der erste Knoten in der zweiten Knotenmenge in der ersten Knotenmenge nicht vorkommt, wird eine leere Knotenmenge zurückgegeben. Falls die zweite Knotenmenge leer ist, dann wird die erste Knotenmenge zurückgegeben.

set:distinct ist eine bequeme Art, Duplikate zu entfernen, solange die Gleichheit von Knoten über die Gleichheit von deren Stringwerten definiert ist:

<xsl:varaible name="firstNames" select="set:destinct(person/firstname)"/>

set:leading und set:trailing können Knoten extrahieren, die von anderen Knoten umklammert werden. Im Rezept XSLT aus XSLT generieren wurde z.B. ein komplexer Ausdruck verwendet, um die xslx:elsif- und xslx:else-Knoten zu finden, die zu Ihrem verbesserten xslx:if gehörten. Erweiterungen können diesen Prozess vereinfachen:

<xsl:apply-templates select="set:leading(following-sibling::xslx:else | following-sibling::xslx:elsif, following-sibling::xslx:if)"/>

Dieser Code besagt, dass Sie alle Geschwisterknoten von xslx:else und xslx:elseif auswählen, die nach dem aktuellen Knoten, aber vor dem nächsten xslx:if stehen.

Sie möchten zusätzliche Information über einen Knoten im Quellbaum haben

Xalan enthält Funktionen, mit denen Sie Informationen über den Ort von Knoten im Quellbaum erhalten können. Saxon 6.5.2 enthält nur saxon:systemId und saxon:lineNumber. Diese Funktionen können z.B. bei der Fehlersuche zur Anwendung kommen. Um die Funktionen zu benutzen, setzen Sie das Attribut TransformerFactory source_location auf true, indem Sie entweder den Schalter -L auf der Kommandozeile oder die Methode TransformerFactory.setAttribute( ) benutzen.

systemId( )
systemId(node-set)

Gibt jeweils die System-ID des aktuellen Knotens bzw. des ersten Knotens in der Knotenmenge zurück.

lineNumber( )
lineNumber(node-set)

Gibt jeweils die Zeilennummer im Quelldokument des aktuellen Knotens bzw. des ersten Knotens in der Knotenmenge zurück. Gibt -1 zurück, falls die Zeilennummer unbekannt ist (z.B. dann, wenn die Quelle ein DOM-Dokument ist).

columnNumber( )
columnNumber(node-set)

Gibt jeweils die Spaltennummer im Quelldokument des aktuellen Knotens bzw. des ersten Knotens in der Knotenmenge zurück. Gibt -1 zurück, falls die Spaltennummer unbekannt ist (z.B. dann, wenn die Quelle ein DOM-Dokument ist):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt" xmlns:info="xalan://org.apache.xalan.lib.NodeInfo">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:template match="foo">
    <xsl:comment>Matched a foo on line <xsl:value-of select="info:lineNumber( )"/> and column <xsl:value-of select="info:columnNumber( )"/>.</xsl:comment>
    <!-- ... -->
  </xsl:template>
</xsl:stylesheet>

Sie möchten mit einer relationalen Datenbank interagieren

Eine Schnittstelle von XSLT zu einer relationalen Datenbank eröffnet eine ganz neue Welt an Möglichkeiten. Sowohl Saxon als auch Xalan verfügen über Erweiterungen, mit denen sie SQL unterstützen. Wenn Sie Stylesheets schreiben, die Datenbankinhalte modifizieren, dann verletzen Sie die Regel, dass XSLT keine Seiteneffekte hat.

Achtung!
Michael Kay sagt Folgendes über die SQL-Erweiterungen von Saxon: »Sie sind nicht unbedingt als qualitativ hochwertiges Stück Software für den Einsatz in der Produktion gedacht (es gibt viele Einschränkungen im Design), sondern mehr als Beispiel dafür, wie Erweiterungselemente benutzt werden können, um die Fähigkeiten des Prozessors zu verbessern.«

Saxon stellt einen Datenbankzugang mit Hilfe von fünf Erweiterungselementen zur Verfügung: sql:connect, sql:query, sql:insert, sql:column und sql:close. Alle, die jemals über ODBC oder JDBC mit einer relationalen Datenbank gearbeitet haben, sollten keine Probleme haben, diese Elemente zu benutzen.

<sql:connect driver="jdbc-driver" database="DB-Name" user="Benutzername" password="Benutzerpasswort"/>

Erzeugt eine Datenbankverbindung. Alle Attribute können auch ein Attributwert-Template sein. Das driver-Attribut benennt die JDBC-Treiberklasse, und das database-Attribut muss ein Name sein, den JDBC mit einer vorhandenen Datenbank in Verbindung bringen kann.

<sql:query table="die Tabelle" column="Spaltennamen" where="Where-Klausel" row-tag="Zeilenelementname" column-tag="Spaltenelementname" disable-output-escaping="yes bzw. no"/>

Führt eine Abfrage aus und schreibt das Ergebnis auf den Ausgabebaum, wobei Zeilen und Spalten mit Elementen dargestellt werden. Die Namen dieser Elemente werden jeweils von row-tag und col-tag angegeben. Das column-Attribut kann eine Liste von Spalten enthalten oder *, um alle Spalten anzugeben.

<sql:insert table="Tabellenname">

Führt ein SQL INSERT aus. Die Kindelemente (sql:column) geben die Daten an, die zur Tabelle hinzugefügt werden sollen.

<sql:column name="Spaltenname" select="XPath-Ausdruck"/>

Wird als Kind von sql:insert benutzt. Der Wert kann vom select-Attribut oder durch Auswertung der Kindelemente von sql:column angegeben werden. Allerdings kann in beiden Fällen nur der Stringwert benutzt werden. Das heißt, es gibt keine Möglichkeit, andere standardisierte SQL-Datentypen zu benutzen.

Die SQL-Unterstützung von Xalan ist umfangreicher als die von Saxon. In diesem Kapitel werden jedoch nur die Grundlagen behandelt. Im Abschnitt »Siehe auch« finden Sie weitere Hinweise auf tiefergehende Details. Im Gegensatz zu Saxon benutzt Xalan Erweiterungsfunktionen für den Zugang zu relationalen Datenbanken.

sql:new(driver, db, user, password)

Stellt eine Verbindung her.

sql:new(nodelist)

Stellt eine Verbindung her und benutzt dabei Angaben in XML-Form, die im Eingabedokument oder dem Stylesheet eingebettet sind. Beispiel:

<DBINFO>
  <dbdriver>org.enhydra.instantdb.jdbc.idbDriver</dbdriver>
  <dburl>jdbc:idb:../../instantdb/sample.prp</dburl>
  <user>jbloe</user>
  <password>geron07moe</password>
</DBINFO>
<xsl:param name="cinfo" select="//DBINFO"/>
<xsl:variable name="db" select="sql:new($cinfo)"/>
sql:query(xconObj, sql-query)

Fragt die Datenbank ab. Das xconObj wird von new( ) zurückgegeben. Die Funktion gibt eine als Stream verwendbare Ergebnismenge in Form eines Zeilenmengenknotens zurück. Durch diese Zeilenmenge können Sie sich zeilenweise durcharbeiten. Dabei wird immer das gleiche Zeilenelement verwendet, d.h., Sie können mit der Transformation der Zeilenmenge beginnen, noch bevor die gesamte Ergebnismenge zurückgegeben wird.

sql:pquery(xconObj, sql-query-with-params)
sql:addParameter(xconObj, paramValue)
sql:addParameterFromElement(xconObj, element)
sql:addParameterFromElement(xconObj, node-list)clearParameters(xconObj)

Werden zusammen bei der Implementierung von parametrisierten Abfragen benutzt. Parameter haben die Form von ?-Zeichen, die in der Abfrage eingebettet sind. Die verschiedenen addParameter( )-Funktionen setzen diese Parameter auf bestimmte Werte, bevor die Abfrage ausgeführt wird. Nach einem clearParameters( ) vergisst das Verbindungsobjekt alle vorherigen Werte.

sql:close(xconObj)

Beendet die Verbindung zur Datenbank.

Die Erweiterungsfunktionen sql:query( ) und sql:pquery( ) geben einen Dokumentknoten zurück, der (wie es benötigt wird) ein Array mit Spalten-Header-Elementen, ein einzelnes Zeilenelement, das wiederholt benutzt wird, sowie ein Array von col-Elementen enthält. Alle Spalten-Header-Elemente (eines pro Spalte in der Zeilenmenge) enthalten ein Attribut (ColumnAttribute) für jeden Spalten-Deskriptor im ResultSetMetaData-Objekt. Alle col-Elemente enthalten einen Textknoten mit einer Textdarstellung des Werts für diese Spalte in der aktuellen Zeile.

Weitere Information über den Einsatz von XSLT beim Zugriff auf relationale Datenbanken finden Sie in Doug Tidwells Buch XSLT (O'Reilly, 2001).

Sie möchten einen zur Laufzeit erzeugten XPath-Ausdruck dynamisch auswerten

Saxon und Xalan verfügen beide über eine sehr mächtige Erweiterungsfunktion namens evaluate, die einen an sie übergebenen String als XPath-Ausdruck auswertet. Von EXSLT.org wird außerdem dyn:evaluate( ) definiert, mit der Sie eine höhere Portabilität erhalten. Ein solches Merkmal wurde bei XSLT 2.0 in Erwägung gezogen, aber damals hat die XSLT 2.0-Arbeitsgruppe beschlossen, es nicht weiter zu verfolgen. Ihre Begründung lautet, dass eine dynamische Auswertung »...erhebliche Konsequenzen für die Laufzeitarchitektur des Prozessors hat, wie auch für die Möglichkeiten zur statischen Optimierung«.

Dynamische Fähigkeiten können sehr praktisch sein, wenn es darum geht, ein tabellengesteuertes Stylesheet zu erzeugen. Das folgende Stylesheet kann Angaben zu Personen in Tabellenform bringen, aber Sie können es so anpassen, dass es mit einer fast unendlichen Menge verschiedener XML-Formate umgehen kann, einfach indem Sie Einträge in einer Tabelle ändern:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://icl.com/saxon" xmlns:paths="http://www.ora.com/XSLTCookbook/NS/paths" exclude-result-prefixes="paths">
  <xsl:output method="html"/>
  <!-- Dieser Parameter gibt ein Dokument mit einer Tabelle an, die beschreibt, wie man Informationen über Leute findet. -->
  <xsl:param name="pathsDoc"/>
  <xsl:template match="/">
    <html>
      <head>
        <title>People</title>
      </head>
      <body>
        <!-- Wir laden einen Xpath-Ausdruck aus einer Tabelle [Symbol_Wingdings_224] in einem externen Dokument. -->
        <xsl:variable name="peoplePath" select="document($pathsDoc)/*/paths:path[@type='people']/@xpath"/>
        <table>
          <tbody>
            <tr>
              <th>First</th>
              <th>Last</th>
            </tr>
            <!-- Werte dynamisch den XPath aus, der Informationen über alle Personen findet. -->
            <xsl:for-each select="saxon:evaluate($peoplePath)">
              <xsl:call-template name="process-person"/>
            </xsl:for-each>
          </tbody>
        </table>
      </body>
    </html>
  </xsl:template>
  <xsl:template name="process-person">
    <xsl:variable name="firstnamePath" select="document($pathsDoc)/*/paths:path[@type='first']/@xpath"/>
    <xsl:variable name="lastnamePath" select="document($pathsDoc)/*/paths:path[@type='last']/@xpath"/>
    <tr>
      <!-- Werte dynamisch den Xpath aus, der die personenspezifischen Angaben findet, die wir verarbeiten möchten. -->
      <td><xsl:value-of select="saxon:evaluate($firstnamePath)"/></td>
      <td><xsl:value-of select="saxon:evaluate($lastnamePath)"/></td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

Diese Tabelle können Sie verwenden, um personenbezogene Daten zu bearbeiten, die als Elemente kodiert sind:

<paths:paths
  xmlns:paths="http://www.ora.com/XSLTCookbook/NS/paths">
  <paths:path type="people" xpath="people/person"/>
  <paths:path type="first" xpath="first"/>
  <paths:path type="last" xpath="last"/>
</paths:paths>

Fügen Sie diese Tabelle hinzu, um personenbezogene Daten zu bearbeiten, die als Attribute kodiert sind:

<paths:paths xmlns:paths="http://www.ora.com/XSLTCookbook/NS/paths">
  <paths:path type="people" xpath="people/person"/>
  <paths:path type="first" xpath="@first"/>
  <paths:path type="last" xpath="@last"/>
</paths:paths>

Sie möchten den Wert einer Variablen ändern

Fast alle Bücher, die Sie zu XSLT lesen werden, beschreiben die Unmöglichkeit, Werte von Variablen und Parametern zu ändern, nachdem sie einmal gebunden sind, eher als eine Fähigkeit statt eines Fehlers von XSLT. Das stimmt, weil damit eine bestimmte Kategorie von Fehlern vermieden wird, Stylesheets leichter verständlich und bestimmte Performance-Optimierungen möglich werden. Manchmal ist diese Unfähigkeit, Werte zu verändern, aber einfach nur lästig. Saxon bietet mit dem Erweiterungselement saxon:assign einen Weg, dieses Hindernis zu umgehen. Sie können saxon:assign jedoch nur bei Variablen benutzen, die mit dem Erweiterungsattribut saxon:assignable="yes" als zuweisbar ausgezeichnet wurden:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:saxon="http://icl.com/saxon" extension-element-prefixes="saxon">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
  <xsl:variable name="countFoo" select="0" saxon:assignable="yes"/>
  <xsl:template name="foo">
    <saxon:assign name="countFoo" select="$countFoo + 1"/>
    <xsl:comment>This is invocation number <xsl:value-of select="$countFoo"/> of template foo.</xsl:comment>
  </xsl:template>
  <!- ... -->
</xsl:stylesheet>

Sie möchten Erweiterungsfunktionen als Bürger erster Klasse in XSLT 1.0 schreiben

Viele Beispiele in diesem Buch sind als benannte Templates (engl. named templates) implementiert, auf die mit xsl:call-template zugegriffen wird. Diese Implementierung ist oftmals lästig und umständlich, weil Sie eigentlich auf diesen Code in Form von echten Funktionen zugreifen möchten, die genauso leicht aufgerufen werden können wie native XPath-Funktionen. In XSLT 2.0 ist das möglich, aber in 1.0 müssen Sie überlegen, ob Sie dafür eine EXSLT-Erweiterung namens func:function benutzen wollen, die in Saxon und der aktuellen Version von Xalan (Version 2.3.2) implementiert ist. Beim folgenden Code handelt es sich um ein Template aus Teilstrings vom Ende eines Strings suchen, das als Funktion reimplementiert wird:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="http://exslt.org/functions" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings" extension-element-prefixes="func">
  <xsl:template match="/">
    <xsl:value-of select="str:substring-before-last('123456789a123456789a123','123')" />
  </xsl:template>
  <func:function name="str:substring-before-last">
    <xsl:param name="input"/>
    <xsl:param name="substr"/>
    <func:result>
      <xsl:if test="$substr and contains($input, $substr)">
        <xsl:variable name="temp" select="substring-after($input, $substr)" />
        <xsl:value-of select="substring-before($input, $substr)" />
        <xsl:if test="contains($temp, $substr)">
          <xsl:value-of select="concat($substr,str:substring-before-last($temp, $substr))"/>
        </xsl:if>
      </xsl:if>
    </func:result>
  </func:function>
</xsl:stylesheet>

XSLT 2.0

Die meisten in Saxon 6 verfügbaren Erweiterungen gibt es auch in Saxon 8. Einige jedoch, wie z.B. saxon:function( ), werden in XSLT 2.0 nicht mehr benötigt. Einige weitere Funktionen gibt es, damit man mehr Möglichkeiten bei XQuery hat, weil diese in XSLT bereits existieren. So erzielen z.B. saxon:index( ) und saxon:find( ) ähnliche Ergebnisse wie die XSLT-Schlüsselfunktion (xsl:key und key( )). Allerdings gibt es einige zusätzliche Bonbons in Saxon 8, die in älteren Produktversionen nicht verfügbar waren.

Sie möchten einen XPath-Ausdruck zum aktuellen Knoten erhalten

Die Funktion saxon:path( ) bekommt keine Argumente und gibt einen String zurück, dessen Wert ein XPath-Ausdruck auf den Kontextknoten ist. Das ist ähnlich zur XSLT-Lösung (die im Rezept Den Stylesheet-Fluss durch sein Eingabedokument mitverfolgen zur Fehlersuche eingeführt wird).

Sie möchten Laufzeitfehler behandeln und abfangen

Viele moderne Sprachen wie Java und C++ verfügen über einen Try-throw-catch-Mechanismus zur Behandlung dynamischer Fehler, also solchen zur Laufzeit. Saxon enthält in seiner kommerziellen Version (Saxon-SA) eine saxon:try-Pseudofunktion, die ähnliche, wenn auch eingeschränktere Möglichkeiten bietet. saxon:try erwartet einen Ausdruck als erstes Argument. Dieser Ausdruck wird ausgewertet, und falls ein Laufzeitfehler auftritt (z.B. eine Division durch null, ein Typfehler etc.), dann wird der Wert des zweiten Arguments zurückgegeben:

<xsl:template match="/">
  <test>
    <xsl:value-of select="saxon:try(*[0], 'Index out of bounds')"/>
  </test>
</xsl:template>

Der Wert dieses zweiten Arguments könnte ein Fehlerstring sein, wie in diesem Beispiel, oder ein vorgegebener Wert. Michael Kay bezeichnet saxon:try als Pseudofunktion, weil sie die Regeln einer normalen XPath-Funktion insofern nicht befolgt, als sie das zweite Argument nur dann auswertet, wenn die Auswertung des ersten fehlschlägt.

Diskussion

Die Benutzung von herstellerspezifischen Erweiterungen hat ihre Vor- und Nachteile. Einerseits erhalten Sie damit die Möglichkeit, eine XSLT-Lösung schneller oder einfacher zu erstellen, als es der Fall wäre, wenn Sie sich auf Standard-XSLT beschränken würden. In einigen Fällen können Sie damit Dinge machen, die mit Standard-XSLT unmöglich sind. Andererseits können Sie damit auf eine Implementierung mit ungewisser Zukunft festgenagelt werden.

EXSLT.org ermutigt die Hersteller dazu, einheitliche Konventionen bei den beliebtesten Erweiterungen einzuhalten, d.h., Sie sollten auf jeden Fall eine EXSLT-Lösung einer herstellerspezifischen Lösung vorziehen, sofern Sie die Wahl haben.

Eine andere Taktik besteht darin, herstellerspezifische Implementierungen gänzlich zu vermeiden, indem Sie Ihre eigene Implementierung schreiben. Auf diese Weise haben Sie die volle Kontrolle über die Quellen und können die Erweiterung, wenn nötig, auf mehr als einen Prozessor portieren. Solche eigenen Erweiterungen werden in den Rezepten Saxon-Erweiterungselemente, Xalan-Java 2-Erweiterungsfunktionen und Java-Erweiterungsfunktion mit Namensraum im Klassenformat behandelt.

Siehe auch

In diesem Buch wurden nicht alle in Saxon und Xalan verfügbaren Erweiterungen behandelt. Zusätzliche Information über die Eigenschaften von Saxon-Erweiterungen finden Sie unter SAXON Extensions bzw. Saxonica Documentation (XSLT 2.0). Weitere Informationen über Xalan-Erweiterungen finden Sie unter Xalan Extensions library.

  

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de