xsl:function

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

A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z

 

Das Toplevel-Element xsl:function dient zur User-Definition von Funktionen, die innerhalb des Stylesheets während dessen Laufzeit in Template-Regeln eingesetzt werden können (Stylesheetfunktionen).

Klassifizierung Deklaration
Funktionsgruppe Stylesheetfunktion definieren
Einführung XSLT 2.0

Position im Stylesheet und erlaubte Inhalte:

xsl:function tritt als Toplevel-Element nur als Kindelement von entweder xsl:stylesheet oder xsl:transform auf.

Eine xsl:function-Deklaration darf eine beliebige Anzahl von xsl:param-Elementen enthalten, entsprechend der Zahl der beim Aufruf zu übergebenden Werte. Es folgt ein Sequenzkonstruktor, der XSLT-Instruktionen (auch Erweiterungsinstruktionen) in beliebiger Anzahl und Mischung enthalten darf. Nach dem letzten xsl:param-Element dürfen optional xsl:message-Elemente eingeschoben werden, die für Debugging-Zwecke o. Ä. benötigt werden.

Attribute:

Es gelten die üblichen Standardattribute. Zusätzlich Die besitzt die Deklaration xsl:function drei Attribute – das obligatorische name-Attribut und die optionalen Attribute as und override.

as

Wert

sequence-type

Verwendung

Optional

Einführung

XSLT 2.0

Das as-Attribut bezeichnet für den Rückgabewert der Stylesheetfunktion einen vorgegebenen Sequenztyp, der Wert des Attributs ist daher ein Sequenztyp-Ausdruck.

Ist das as-Attribut gesetzt, so wird das Ergebnis des Funktionsaufrufs gemäß der Typkonvertierungsregeln für Funktionen (function conversion rules) in den angegebenen Typ konvertiert. (Anmerkung: Siehe die XPath 2.0-Spezifikation, Abschnitt 3.1.5ff, Function conversion rules). Schlägt der Versuch dieser Konvertierung fehl, so gilt dies als Typfehler (ERR XTTE0780). Wird das as-Attribut nicht eingesetzt, so bleibt das Funktionsergebnis unverändert.

name

Wert

QName

Verwendung

Obligatorisch

Einführung

XSLT 2.0

Das obligatorische name-Attribut legt den Bezeichner der Funktion fest, wie er für den Aufruf verwendet werden soll. Damit es in keinem Fall zu Konflikten mit den bestehenden Systemfunktionen kommen kann, muss es sich bei dem gewählten Bezeichner um einen QName mit Präfix handeln – es gilt als statischer Fehler, wenn ein präfixloser Bezeichner verwendet wird (ERR XTSE0740).

Das Präfix kann an einen beliebig wählbaren Namensraum gebunden sein, der jedoch »nicht null« sein muss. Verboten ist jedoch die Verwendung reservierter Namensräume (hierunter fällt der XSLT-Namensraum, der XML-Namensraum sowie verschiedene Namensräume in Zusammenhang mit XML Schema) – der Verstoß hiergegen bewirkt wiederum einen statischen Fehler (ERR XTSE0080).

override

Wert

"yes" | "no"

Verwendung

Optional

Einführung

XSLT 2.0

Das override-Attribut kann wahlweise mit den Werten "yes" oder "no" eingesetzt werden, um festzulegen, ob die selbst definierte Funktion vorrangig vor einer gleichnamigen, durch den Her­steller des Prozessors implementierten Funktion verwendet werden soll oder nicht.

Defaultwert des Attributs ist "yes". Dies bedeutet, dass die selbst definierte Funktion Vorrang besitzt, falls man das override-Attribut weglässt, aber auch, wenn es explizit auf "yes" gesetzt wird. Das Stylesheet wird sich für alle XSLT-Prozessoren identisch verhalten, auch für solche mit implementierten Herstellerfunktionen gleichen Namens (die dann übergangen werden).

Wird dagegen der Wert "no" verwendet, so wird vorrangig auf eine bereits imple­mentierte, gleichnamige Funktion zurückgegriffen. Dies ist dann sinnvoll, wenn die Möglichkeit besteht, dass das Stylesheet durch XSLT-Prozessoren verarbeitet werden muss, in denen eine entsprechende Erweiterungsfunktion nicht imp­lementiert ist. Entsprechende Prozessoren greifen dann auf die Stylesheetfunktion zurück, die somit die Aufgabe einer Fallback-Deklaration übernimmt. Für die eigentliche Zielplatt­form wird hingegen die durch den Hersteller vorgesehene Funktion verwendet.

Verwendungszweck:

Das Toplevel-Element xsl:function dient dazu, eine sogenannte Stylesheetfunktion zu deklarieren. Deklariert werden können bzw. müssen der Bezeichner der Funktion, die zu übergebenen Parameter und ihre jeweiligen Typen sowie die eigentliche Implementierung der Funktionalität (Funktionsrumpf). Die Funktionsdeklaration findet im Toplevel-Bereich statt; innerhalb der Deklaration darf daher auf globale Variable und globale Para­meter zugegriffen werden, da diese zu diesem Zeitpunkt als existent gelten. Tunnelparameter sind dagegen genau wie lokale Variable oder Parameter für die Stylesheetfunktion nicht zugänglich.

Aufgerufen werden kann eine so definierte Funktion in beliebigen XPath-Aus­drücken. Hierzu zählen auch XPath-Pattern und deren Predicates innerhalb der Template-Re­geln des Stylesheets.

Zu beachten ist, dass im Inneren der Funktion keine Möglichkeit besteht, auf den Kontext des Aufrufs zuzugreifen: Der Fokus der Funktion ist per se »undefiniert«– es ist während der Auswertung der Funktion daher weder das Kontextitem noch die Kontextposition oder Kontextgröße des Aufrufs zugänglich. Aus dem gleichen Grund sind auch eventuelle lokalen Variablen aus dem Kontext des Aufrufs innerhalb des Funktionskörpers nicht verfügbar (deren Werte können aber als Parameter übergeben werden).

Die Erzeugung der Rückgabesequenz kann auf beliebige Art und Weise erfolgen. Als einfache Variante bietet sich der Einsatz der Instruktion xsl:sequence an, die Sequenzen beliebiger Zusammensetzung generieren kann. Alternativ kann auf jede andere Instruktion zurückgegriffen werden, die eine Sequenz zurückgibt (beispielsweise xsl:perform-sort), oder auf einen, aus mehreren Instruktionen bestehenden, Sequenzkonstruktor. Mit Hilfe des as-Attributs von xsl:function kann ein vorgegebe­ner Datentyp für die Rückgabesequenz forciert werden.

Der Bezeichner der Funktion muss ein QName sein, dessen Namensraum für die Position der Deklaration im Scope sein muss, also sinnvollerweise im xsl:stylesheet-Wurzelelement deklariert wird. Dieser Namensraum kann, um dessen ungewolltes Kopieren ins Ergebnisdokument zu vermeiden, hiervon mit Hilfe des exclude-result-prefixes-Attributs von xsl:stylesheet ausgeschlossen werden.

Definition und Zuordnung der Übergabeparameter:
Innerhalb der Deklaration xsl:function werden zunächst xsl:param-Ele­mente definiert, deren Anzahl und Reihenfolge den beim Aufruf der Funktion zu übergebenden Werten entspricht. Das erste übergebene Argument wird dem ersten Parameter zugeordnet und so fort.

<xsl:function name="meine:funktion">
  <xsl:param name="param1"/>
  <xsl:param name="param2"/>
  ...
</xsl:function>

wird aufgerufen mit:

meine:funktion($wert_fuer_param1, $wert_fuer_param2)

Eine Zuordnung über den Parameter­namen ist von außen also nicht möglich, dieser dient lediglich im Inneren der Funk­tion zur Referenzierung des Wertes. Der Typ des zu übergebenden Wertes kann für jedes xsl:param-Element jeweils mit Hilfe dessen as-Attributs festgelegt werden.

<xsl:function name="meine:funktion">
  <xsl:param name="param1" as="xs:decimal" />
  <xsl:param name="param2" as="xs:decimal" />
  <!-- Werte addieren: -->
  <xsl:sequence select="$param1 + $param2"/>
</xsl:function>

Akzeptiert werden auch Subtypen des geforderten Typs, ohne dass eine Umwandlung vorgenommen wird – übergibt man im vorliegenden Fall eine Zahl vom Typ xs:integer, einem Subtyp von xs:decimal, so behält diese ihren Typ. Liegt ein Wert hingegen nicht im ver­langten Typ vor, so wird zunächst versucht, ihn entsprechend umzuwandeln. Erst wenn dies nicht gelingt, ist ein Laufzeitfehler die Folge (ERR XTTE0790). Wird keine Vorgabe durch as gegeben, so wird jeder beliebige übergebene Typ akzeptiert.

Anforderungen an die Parameterdefinitionen:
Optionale Parameter können nicht definiert werden. Für einen in der Funktion deklarierten Funktionsparameter muss daher zwingend ein Wert übergeben werden (und wenn es die leere Sequenz ist). Da die Wertübergabe hier erforderlich (mandatory) ist, darf im Rahmen der Parametervereinbarung kein Defaultwert gesetzt werden: Das xsl:param-Element muss daher leer sein, auch sein select-Attribut darf nicht verwendet werden (dies gilt grundsätzlich für Pflichtparameter, nicht nur in diesem Zusammenhang).

Ebensowenig darf das required-Attribut von xsl:param für Funktionsparameter eingesetzt werden. Auch das tunnel-Attribut ist verboten, da Stylesheetfunktionen ohnehin keine Werte aus dem Tunnelstrom entnehmen können (was wiederum Kontextgründe hat).

Jede gültige Deklaration einer Stylesheetfunktion fügt diese dem statischen Kontext des Stylesheets hinzu, wodurch sie überall im Stylesheet in XPath-Ausdrücken aufgerufen werden kann. Dies gilt allerdings nur solange, wie keine gleichnamige Funktion mit höherer Importpräferenz existiert (die beispielsweise in einem höherrangigen Modul deklariert wurde), oder, sofern override="no" gesetzt wurde, eine gleichnamige Erweiterungsfunktion existiert.

Funktionserkennung mittels Argumentzahl:
Hierbei ist es wichtig, dass eine Stylesheetfunktion neben ihrem Namen auch anhand der Zahl ihrer Argumente (arity) identifiziert wird. Das vorgenannte trifft also nur dann zu, wenn zwei Funktionen die gleiche Argumentzahl besitzen. Andernfalls ist eine Koexistenz zweier gleichnamiger Funktionen verschiedener Argumentzahl möglich:

<!-- Funktion 1 mit zwei Argumenten: -->
<xsl:function name="kein:problem">
  <xsl:param name="param1" as="xs:decimal" />
  <xsl:param name="param2" as="xs:decimal" />
  <!-- Werte addieren: -->
  <xsl:sequence select="$param1 + $param2"/>
</xsl:function>
<!-- Funktion 2 mit drei Argumenten: -->
<xsl:function name="kein:problem">
  <xsl:param name="param1" as="xs:decimal" />
  <xsl:param name="param2" as="xs:decimal" />
  <xsl:param name="param3" as="xs:decimal" />
  <!-- Werte addieren: -->
  <xsl:sequence select="$param1 + $param2 + $param3"/>
</xsl:function>

Beide Funktionen können beim Aufruf durch die Zahl der Argumente unterschieden werden:

kein:problem($p1, $p2)      <!-- Aufruf Funktion 1 -->
kein:problem($p1, $p2, $p3) <!-- Aufruf Funktion 2 -->

Keine Polymorphie:
Da der Typ der übergebenen Werte nicht beim Aufruf geprüft wird, sondern erst bei der Übernahme der Parameter in der Funktion, ist es allerdings nicht möglich, zwei gleichnamige Funktionen anhand der geforderten Argumenttypen zu unterschieden (Polymorphie ist also nicht gegeben). Es ist daher grundsätzlich ein statischer Fehler, zwei gleichnamige Funktionen mit gleicher Argumentzahl einzuführen (ERR XTSE0770). Der Fehler kommt jedoch nur zum Tragen, wenn beide Funktionen zudem gleiche Importpräzedenz besitzen und nicht eine weitere, höherrangige Funktion gleicher Signatur existiert (Vorsicht daher bei der Inklusion von Stylesheetmodulen mittels xsl:include).

Beispiele:

Beispiel 1 – Zahleneingabe in römische Ziffer umwandeln:

<xsl:function name="bsp:roemisch">
  <xsl:param name="eingabezahl" as="xs:integer"/>
  <xsl:variable name="roemische-zahl">
    <xsl:number value="$eingabezahl" format="I"/>
  </xsl:variable>
  <xsl:sequence select="$roemische-zahl" as="xs:string"/>
</xsl:function>

Diese Funktion wandelt eine ihr übergebene Ganzzahl (Typ xs:integer) in eine römische Ziffer um und gibt diese als String zurück. Ein Aufruf könnte z.B. folgendermaßen innerhalb einer Template-Regel erfolgen:

...
<xsl:value-of select="bsp:roemisch(@baujahr)"/>
...

Hierbei soll das Attribut baujahr eine Jahreszahl enthalten. Für bau­jahr="1850" wird entsprechend der String »MDCCCL« zurückgegeben. Die eigentliche Umwandlung erfolgt durch xsl:number mit der Formatierung in großen römischen Ziffern format="I". Deren Rückgabewert kann jedoch nicht unmittelbar verwendet werden, sondern muss in eine Variable zwischenge­speichert werden.

<xsl:variable name="roemische-zahl">
  <xsl:number value="$eingabezahl" format="I"/>
</xsl:variable>

Innerhalb des select-Attributs von xsl:sequence wird nun eine Referenz auf die Variable verwendet, um das Ergebnis an den Ort des Funktionsaufrufs zurückzuliefern:

<xsl:sequence select="$roemische-zahl" as="xs:string"/>

Das as-Attribut sorgt zwingend dafür, dass der zurückgelieferte Wert ein String ist – hierfür sorgt allerdings bereits xsl:number, das von vornherein Strings erzeugt. Unterbliebe das as-Attribut, so würde keine Wertkonvertierung erfol­gen.

Beispiel 2 – Reihenfolge von Worten in einem Satz umkehren:

Im folgenden Stylesheet wird eine Funktion verwendet, die die Reihenfolge der Worte eines ihr übergebenen Satzes umkehrt. Der Satz ist hier als statischer Wert realisiert, könnte aber auch aus einem globalen Parameter stammen.

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:bsp="http://beispiel.com/namespace" exclude-result-prefixes="bsp">
  <xsl:function name="bsp:umkehren">
    <xsl:param name="satz" as="xs:string"/>
    <xsl:sequence select="if (contains($satz, ' ')) then concat(bsp:umkehren(substring-after($satz, ' ')),' ', substring-before($satz, ' ')) else $satz" as="xs:string"/>
  </xsl:function>
  <xsl:template match="/">
    <ausgabe>
      <xsl:value-of select="bsp:umkehren('Eins Zwei Drei Vier')"/>
    </ausgabe>
  </xsl:template>
</xsl:stylesheet>

Ausgabe dieses Stylesheets ist:

<ausgabe>Vier Drei Zwei Eins</ausgabe>

Der Namensraum der Funktion wird im Stylesheetelement vereinbart und vom Kopieren in das Ergebnisdokument ausgeschlossen:

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:bsp="http://beispiel.com/namespace"
  exclude-result-prefixes="bsp"
>

Die Funktion selbst übernimmt einen Satz als Parameter, die eigentliche Arbeit spielt sich jedoch erst im select-Attribut von xsl:sequence ab. XPath 2.0 erlaubt die Formulierung von IF-ELSE-Bedingungen innerhalb eines Aus­drucks, was hier eingesetzt wird. Zunächst wird geprüft, ob »if« der überge­bene String ein Leerzeichen enthält, also aus einzelnen Worten besteht. Ist dies nicht der Fall, so wird der Eingabewert »else« unmittelbar zurückgegeben:

select="if (contains($satz, ' ')) then ... else $satz"

Interessant ist der then-Zweig, der genommen wird, wenn der Wert ein oder mehrere Leerzeichen enthält – die Funktion beruht darauf, dass die Funktion sich rekursiv selbst aufruft:

then concat(bsp:umkehren(substring-after($satz, ' ')),' ', substring-before($satz,' '))

Die Rekursion tritt auf, solang der substring-after()-Teil des Eingabewertes (der bei jedem Durchlauf um das erste Wort verkürzt wird!) noch ein Leerzei­chen enthält – ist dies nicht mehr der Fall, so tritt der else-Zweig innerhalb der Funktion in Kraft und bricht die Kette ab. Das select-Attribut von xsl:sequence enthält nun den vollständig invertierten Satz und gibt ihn an den Ort des Aufrufs zurück.

Beispiel 3 – Umgeben eines Strings mit runden Klammern:

Stylesheet:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fb="http://www.webdimensions.de/func" exclude-result-prefixes="fb">
  <xsl:function name="fb:einklammern">
    <xsl:param name="eingabe"/>
    <xsl:sequence select="concat('(',$eingabe,')')"/>
  </xsl:function>
  <xsl:template name="einklammern">
    <ergebnis>
      <xsl:value-of select="fb:einklammern('Beispiel')"/>
    </ergebnis>
  </xsl:template>
...

Ergebnis:

<ergebnis>(Beispiel)</ergebnis>

Die Manipulation des Eingabestrings, d.h. die Verkettung mit runden Klam­mern, findet vollständig innerhalb des XPath-Ausdrucks des select-Attributs von xsl:sequence statt. Die Funktion wird aus xsl:value-of aufgerufen, die den Wert in das Ergebnisdokument ausgibt. (Anmerkung: Dasselbe Ergebnis ließe sich genauso gut unter XSLT 1.0 mit einem benannten Template darstellen.)

Beispiel 4 – Berechnen der Fakultät einer Zahl:

Stylesheet:

<xsl:function name="fb:fakultaet">
  <xsl:param name="zahl" type="xsd:integer"/>
  <xsl:sequence select="if($zahl=1) then 1 else $zahl * fb:fakultaet($zahl – 1)"/>
</xsl:function>

Die Funktion berechnet die Fakultät einer ihr beim Aufruf übergebenen Zahl. Innerhalb des im select-Attribut von xsl:sequence vorliegenden XPath-Aus­drucks ruft sich die Funktion selbst rekursiv wieder auf. Dies ist vollkommen legal, da der Aufruf einer Stylesheetfunktion innerhalb jedes XPath-Ausdrucks erfolgen darf, auch wenn sich dieser innerhalb der aufgerufenen Funktion selbst befindet.

Elementdefinition:

XSLT 1.0:

Element in XSLT 1.0 nicht verfügbar.

XSLT 2.0:

<!-- Category: declaration -->
<xsl:function
     name = qname
     as? = sequence-type
     override? = "yes" | "no">

     <!-- Content: (xsl:param*, sequence-constructor) -->
</xsl:function>
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