Již při uvolnění finální specifikace XSLT 1.0 bylo jasné, že několik důležitých funkcí ve standardu chybí – např. výstup do více souborů, operace s množinami uzlů, převod fragmentu XML zpět na množinu uzlů apod. Jednotlivé procesory proto začaly nabízet vlastní implementace, které však nebyly kompatibilní, protože se jinak jmenovaly a občas se drobně lišily svým chováním. Vývojáři XSLT se proto dohodli a definovali sadu nejpoužívanějších rozšiřujících funkcí a instrukcí. Iniciativa je známá jako EXSLT. Mnoho z těchto funkcí bylo později standardizováno v XSLT 2.0 a XPath 2.0.
EXSLT definuje nové funkce a instrukce v několika skupinách. Některé XSLT procesory (zejména Saxon a xslproc) většinu nebo i všechny funkce a instrukce implementují přímo. V jiných je možné použít implementace vytvořené pomocí různých vnořených skriptů apod. Styly používající EXSLT samozřejmě nebudou pracovat ve všech XSLT procesorech, ale budou rozhodně přenositelnější, než kdybychom používali pouze rozšíření specifická pro určitý procesor.
V následujícím přehledu se podíváme na funkce a instrukce EXSLT a u těch nejdůležitějších si na příkladech ukážeme, k čemu je použít. Funkce jsou členěny do logických kategorií, každá kategorie má svůj jmenný prostor. Zařadil jsem pouze ty funkce, které jsou povinné u EXSLT implementací. EXSLT obsahuje ještě další experimentální funkce (např. pro práci s regulárními výrazy) – jejich popis naleznete na stránkách iniciativy.
http://exslt.org/common
exsl
Převede fragment výstupního XML dokument zpět na seznam uzlů.
seznam uzlů exsl:node-set(
objekt)
Jedná se o jedno z nejpoužívanějších rozšíření. Umožňuje například provádění víceprůchodových transformací během jednoho běhu procesoru apod. My si použití ukážeme na jednoduchém příkladu výpočtu celkové hodnoty objednávky.
Příklad 13.1. Dokument s objednávkou – objednavka.xml
<?xml version="1.0" encoding="utf-8"?>
<objednavka>
<jmeno>Pepa</jmeno>
<datum>dnes</datum>
<polozka>
<nazev>Rohlík</nazev>
<pocet>10</pocet>
<cena>2</cena>
</polozka>
<polozka>
<nazev>Pivo</nazev>
<pocet>6</pocet>
<cena>7.5</cena>
</polozka>
<polozka>
<nazev>Týdeník Respekt</nazev>
<pocet>1</pocet>
<cena>20</cena>
</polozka>
</objednavka>
Předpokládejme, že chceme zjistit celkovou cenu objednávky.
Funkce sum()
nám v tomto případě neposlouží,
protože umí sčítat jen seznam uzlů. My však potřebujeme sečíst součiny
ceny a počtu kusů. V čistém XSLT 1.0 si můžeme pomoci rekurzivní
funkcí.
Příklad 13.2. Součet součinů řešený pomocí rekurze – objednavka.xsl
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <!-- Sečtení objednávky --> <xsl:template match="/"> <xsl:variable name="nadpis"> Objednávka pro <xsl:value-of select="objednavka/jmeno"/> ze dne <xsl:value-of select="objednavka/datum"/> </xsl:variable> <html> <head> <title><xsl:value-of select="$nadpis"/></title> </head> <h1><xsl:value-of select="$nadpis"/></h1> <table border="1"> <tr> <th>Název</th> <th>Ks</th> <th>Cena</th> <th>Celkem</th> </tr> <xsl:for-each select="objednavka/polozka"> <tr> <td><xsl:value-of select="nazev"/></td> <td><xsl:value-of select="pocet"/></td> <td><xsl:value-of select="cena"/></td> <td><xsl:value-of select="pocet * cena"/></td> </tr> </xsl:for-each> <tr> <th>Celkem</th> <th colspan="3"> <xsl:call-template name="secti"> <xsl:with-param name="node" select="objednavka/polozka[1]"/> </xsl:call-template> </th> </tr> </table> </html> </xsl:template> <!-- Rekurzivní výpočet celkového součtu --> <xsl:template name="secti"> <xsl:param name="node" select="."/> <xsl:variable name="sumazbytku"> <xsl:choose> <xsl:when test="$node/following-sibling::polozka"> <xsl:call-template name="secti"> <xsl:with-param name="node" select="$node/following-sibling::polozka[1]"/> </xsl:call-template> </xsl:when> <xsl:otherwise> 0 </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="($node/pocet)*($node/cena) + $sumazbytku"/> </xsl:template> </xsl:stylesheet>
Mnohem jednodušší a průhlednější řešení však používá funkci
exsl:node-set()
. Nejprve si dílčí součiny uložíme
do proměnné jako fragment XML, pak je funkcí převedeme zpět na seznam
uzlů a ten již můžeme zcela běžnými funkcemi sečíst.
Příklad 13.3. Součet součinů pomocí funkce node-set – objednavka2.xsl
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" exclude-result-prefixes="exsl" version="1.0"> <!-- Sečtení objednávky --> <xsl:template match="/"> <xsl:variable name="nadpis"> Objednávka pro <xsl:value-of select="objednavka/jmeno"/> ze dne <xsl:value-of select="objednavka/datum"/> </xsl:variable> <html> <head> <title><xsl:value-of select="$nadpis"/></title> </head> <h1><xsl:value-of select="$nadpis"/></h1> <table border="1"> <tr> <th>Název</th> <th>Ks</th> <th>Cena</th> <th>Celkem</th> </tr> <xsl:for-each select="objednavka/polozka"> <tr> <td><xsl:value-of select="nazev"/></td> <td><xsl:value-of select="pocet"/></td> <td><xsl:value-of select="cena"/></td> <td><xsl:value-of select="pocet * cena"/></td> </tr> </xsl:for-each> <tr> <th>Celkem</th> <th colspan="3"> <!-- Uložení dílčích součtů jako seznamu čísel v XML --> <xsl:variable name="mezisoucty"> <xsl:for-each select="objednavka/polozka"> <cislo><xsl:value-of select="cena * pocet"/></cislo> </xsl:for-each> </xsl:variable> <!-- Převedení XML seznamu na seznam uzlů a jeho sečtení --> <xsl:value-of select="sum(exsl:node-set($mezisoucty)/cislo)"/> </th> </tr> </table> </html> </xsl:template> </xsl:stylesheet>
Při použití rozšiřujících funkcí můžeme styly psát tak, že budou funkční ve všech procesorech. Před použitím rozšiřujících funkcí stačí otestovat, zda je máme k dispozici.
Příklad 13.4. Univerzální součet součinů – objednavka3.xsl
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" exclude-result-prefixes="exsl" version="1.0"> <!-- Sečtení objednávky --> <xsl:template match="/"> <xsl:variable name="nadpis"> Objednávka pro <xsl:value-of select="objednavka/jmeno"/> ze dne <xsl:value-of select="objednavka/datum"/> </xsl:variable> <html> <head> <title><xsl:value-of select="$nadpis"/></title> </head> <h1><xsl:value-of select="$nadpis"/></h1> <table border="1"> <tr> <th>Název</th> <th>Ks</th> <th>Cena</th> <th>Celkem</th> </tr> <xsl:for-each select="objednavka/polozka"> <tr> <td><xsl:value-of select="nazev"/></td> <td><xsl:value-of select="pocet"/></td> <td><xsl:value-of select="cena"/></td> <td><xsl:value-of select="pocet * cena"/></td> </tr> </xsl:for-each> <tr> <th>Celkem</th> <th colspan="3"> <!-- Výběr způsobu výpočtu na základě schopností XSLT procesoru --> <xsl:choose> <xsl:when test="function-available('exsl:node-set')"> <!-- Výpočet s využitím rozšiřující funkce --> <xsl:variable name="mezisoucty"> <xsl:for-each select="objednavka/polozka"> <cislo><xsl:value-of select="cena * pocet"/></cislo> </xsl:for-each> </xsl:variable> <xsl:value-of select="sum(exsl:node-set($mezisoucty)/cislo)"/> </xsl:when> <xsl:otherwise> <!-- Klasická XSLT 1.0 cesta --> <xsl:call-template name="secti"> <xsl:with-param name="node" select="objednavka/polozka[1]"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </th> </tr> </table> </html> </xsl:template> <!-- Rekurzivní výpočet celkového součtu --> <xsl:template name="secti"> <xsl:param name="node" select="."/> <xsl:variable name="sumazbytku"> <xsl:choose> <xsl:when test="$node/following-sibling::polozka"> <xsl:call-template name="secti"> <xsl:with-param name="node" select="$node/following-sibling::polozka[1]"/> </xsl:call-template> </xsl:when> <xsl:otherwise> 0 </xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:value-of select="($node/pocet)*($node/cena) + $sumazbytku"/> </xsl:template> </xsl:stylesheet>