Stylesheet-Funktionen

(Auszug aus "XSLT 2.0 & XPath 2.0" von Frank Bongers, Kapitel 3.)

Eine Annäherung an Funktionen (eigentlich eher Prozeduren) im Sinne eines benannten Blocks von Instruktionen kann durch benannte Templates erreicht werden. Ihr Aufruf kann auf XSLT-Ebene beliebig häufig und aus jeder Templa­teregel per xsl:call-template erfolgen. Ein Aufruf einer solchen »Funktion« in XPath-Ausdrücken ist jedoch nicht möglich. Dieses Manko wurde rasch erkannt, und man sann auf Abhilfe durch externe Spracher­weiterungen.

Viele der von EXSLT [EXSLT – Open Source-Erweiterungen in Modulform zum XSLT 1.0-Sprachkern, die von den meisten XSLT-Prozessoren unterstützt werden] angebotenen Erweiterungsfunktionen sind zwar eben­falls in Form benannter Templates realisiert. Die Definition »echter«, also XPath-tauglicher EXSLT-Funktionen ist aber ebenfalls vorgesehen und geschieht mit Hilfe der Erweiterungsinstruktion exsl:function. Die hiermit geschriebenen Funktionen entsprechen vom Aufbau her einem Templatekör­per, dessen Ergebnis den Rückgabewert der Funktion darstellt. Soweit zur Situ­ation in XSLT 1.0.

XSLT 2.0: Deklaration von Funktionen mit Bordmitteln

Jetzt wird alles besser: Dieses Prinzip der Deklaration einer Funktion über eine eigene Instruktion wurde in XSLT 2.0 in Form von xsl:function in den Sprachkern übernommen.

Jede so erzeugte Funktion besitzt einen eigenen Namen (mit Namensraum), kann potenziell Parameter übernehmen und innerhalb der XPath-Ausdrücke des Stylesheets verwendet werden, in dem die Definition erfolgte. Da die Gül­tigkeit einer solchen Funktion an die Laufzeit des Stylesheets gebunden ist, wird sie auch als Stylesheet-Funktion bezeichnet.

Hier ist ein ganz einfaches Beispiel, das mit einem beliebigen Quelldokument arbeitet – das Stylesheet erzeugt seinen Output vollkommen selbstständig. Aus­gegeben wird eine Umrechnungstabelle von Unze nach Gramm.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:bsp="http://beispielnamensraum">
  <xsl:function name="bsp:unze-gramm">
    <xsl:param name="gewicht"/>
    <xsl:sequence select="$gewicht * 28.349"/>
  </xsl:function>
  <xsl:template match="/">
    <html>
      <head>
        <title>Gewichtstabelle Unze – Gramm</title>
      </head>
      <body>
        <h1>Gewichtstabelle Unze – Gramm</h1>
        <table border="1">
          <tr><th>Unze</th><th>Gramm</th></tr>
          <xsl:for-each select="(1 to 20)">
            <tr>
              <td><xsl:value-of select="."/></td>
              <td><xsl:value-of select="bsp:unze-gramm(.)"/></td>
            </tr>
          </xsl:for-each>
        </table>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

Die Funktion des Beispiels

Die Berechnung des Grammwerts findet in einer selbst definierten Funktion statt. Als Stylesheet-Funktion muss sie einen qualifizierten Bezeichner erhal­ten; der entsprechende Namensraum ist im Stylesheetelement vereinbart. Die­ser Namensraum darf nicht der XSL-Namensraum sein, alles andere ist im Grunde irrelevant. Der Namensraum wird über das vereinbarte Präfix an den Funktions­namen gebunden:

<xsl:function name="bsp:unze-gramm">
  <xsl:param name="gewicht"/>
  <xsl:sequence select="$gewicht * 28.349"/>
</xsl:function>

Die Funktion nimmt einen Wert entgegen und übergibt ihn ins Innere des Funktionsblocks, wo er verarbeitet wird. Der Wert muss beim Aufruf überge­ben werden, der über den QName der Funktion in einem XPath-Ausdruck erfolgt:

<xsl:value-of select="bsp:unze-gramm(.)"/>

In diesem speziellen Fall wird der Funktion eine Ganzzahl übergeben, das Cur­rent Item der umgebenden xsl:for-each-Instruktion. Diese arbeitet mit einer generierten Sequenz aus Ganzzahlen von 1 bis 20, die mit dem Bereichsope­rator to erzeugt wird:

<xsl:for-each select="(1 to 20)">
  <!-- Current Item ist Ganzzahl --> 
  ...
</xsl:for-each>

Dies ist eine simple Methode, um in XSLT eine Zählerschleife zu simulieren, wie sie in anderen Programmiersprachen mit FOR-Schleifen erstellt werden können.

Der Übergabeparameter der Funktion

Der Übergabeparameter wird als normales xsl:param-Element realisiert, wobei eine Besonderheit zu beachten ist, die einen Funktionsparameter von einem gewöhnlichen Parameter unterscheidet.

Ein Funktionsparameter muss leer sein. Da die Übergabe eines Werts obli­gatorisch ist, besteht keine Veranlassung, dass der Parameter selbst einen Defaultwert bereitstellen muss. Die Instruktion muss daher stets leer sein und darf auch kein select-Attribut besitzen.

Aus dem gleichen Grund ist auch das required-Attribut verboten; ebenso ver­bietet sich die Verwendung des tunnel-Attributs. Dies hängt zum einen damit zusammen, dass ohnehin ein expliziter Wert übergeben werden muss, zum anderen aber auch der Tunnelstrom für die Funktion nicht zugänglich ist.

Dies gilt auch für den Funktionsblock. Referenziert werden dürfen dort nur die Funktionsparameter, weder Tunnelparameter noch die Parameter aus dem Scope des aufrufenden Templates sind erreichbar.

Globale Variablen und Parameter sind zugänglich

Auf globale Parameter und Variablen kann die Funktion dagegen sehr wohl zugreifen – hier wurden zwei Umrechnungsfaktoren in globale Variablen abge­legt. Die Funktion gibt eine Sequenz aus zwei Werten zurück: »Ounce« und »Troy-Ounce«:

<!-- globale Variablen: -->
<xsl:variable name="ounce-faktor">28.349</xsl:variable>
<xsl:variable name="troy-faktor">31.103</xsl:variable>
<xsl:function name="bsp:gramm-unze">
  <xsl:param name="gewicht"/>
  <xsl:variable name="gramm" select="$gewicht * 10"/>
  <xsl:variable name="gramm-ounce" select="format-number($gramm div $ounce-faktor,'#0.00')"/>
  <xsl:variable name="gramm-troy" select="format-number($gramm div $troy-fak¬tor,'#0.00')"/>
  <xsl:sequence select="($gramm-ounce, '|', $gramm-troy)"/>
</xsl:function>

Code-Beispiel: kap03/3.06/unze-gramm.xsl (Auszug).

Die Position der globalen Variablendeklaration gegenüber der Funktionsdekla­ration ist irrelevant, denn in diesem Fall greift die Following-Sibling-Regel für den Variablenscope nicht: Zum Zeitpunkt des Funktions­aufrufs – und das ist es, was zählt – ist eine globale Variable in jedem Fall initialisiert.

Selbstbezüglich: Rekursive Stylesheet-Funktionen

Stylesheet-Funktionen dürfen rekursiv sein. Sie können mit ihnen daher ohne Schwierigkeiten eine Funktionalität erzielen, wie sie mit rekursivem Aufruf benannter Templates möglich ist.

Als kleine Demonstration hier die Berechnung der Fakultät einer übergebenen Ganzzahl:

<xsl:function name="bsp:fakultaet">
  <xsl:param name="zahl"/>
  <xsl:choose>
    <xsl:when test="$zahl=1">
      <xsl:sequence select="1"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:sequence select="$zahl * bsp:fakultaet($zahl – 1)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

Code-Beispiel: kap03/3.06/fakultaet.xsl (Auszug).

Die Funktion bildet eine Multiplikationskette der Form n*(n-1)*(n-2)...*(n-(n-1)*1. Hierfür wird intern geprüft, ob die übergebene Zahl die 1 ist, in diesem Fall wird durch xsl:sequence ebenfalls eine 1 zurückgegeben. Andernfalls wird die Eingangszahl mit dem Rückgabewert einer weiteren Instanz der Stylesheetfunktion multipliziert, die den um 1 verringerten Ein­gangswert übergeben bekommt. Am Ende dieser Kette sitzt in jedem Fall eine Funktionsinstanz, die eine 1 als Wert erhält – ab diesem Moment wird die Mul­tiplikation nach rückwärts aufgelöst.

   

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

Copyright © Galileo Press, Bonn 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT 2.0 & XPath 2.0 ― Das umfassende Handbuch" 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.


Galileo Press, Rheinwerkallee 4, 53227 Bonn