Szablon (template) może posiadać nazwę podaną w atrybucie name
.
Wówczas szablon ten można wywołać używając instrukcji call-template
.
Przy takim wywołaniu nie zmienia się kontekst (inaczej niż przy apply-templates
).
Szablon musi posiadać co najmniej jeden z parametrów match
i name
, może
posiadać oba.
Plik: named.xsl.
<xsl:template match="/"> <html> <body> <h1>Wszystkie elementy:</h1> <xsl:for-each select="//*"> <xsl:call-template name="opisz-element"/> </xsl:for-each> <h1>Zagnieżdżone elementy:</h1> <xsl:for-each select="//*/*/*"> <xsl:call-template name="opisz-element"/> </xsl:for-each> </body> </html> </xsl:template> <xsl:template name="opisz-element"> <p>Element o nazwie <xsl:value-of select="name()"/>.</p> </xsl:template>
XSLT pozwala na deklarowanie parametrów i zmiennych, których następnie można używać w wyrażeniach XPath, np.: $nazwa
.
Zmienne w XSLT są deklaratywne (jak w programowaniu funkcyjnym): już w miejscu
deklaracji następuje przypisanie wartości, której następnie nie można modyfikować. Zmienne deklaruje się
w elementach variable
.
Deklaracja zmiennej lokalnej może wystąpić wewnątrz szablonów (wszędzie tam, gdzie mogą występować
inne instrukcje XSLT). Zmienna jest widoczna do końca elementu, w którym została zadeklarowana.
Wartość zmiennej lokalnej może być obliczana wielokrotnie (gdy szablon jest wielokrotnie używany
lub wewnątrz pętli for-each
).
Deklaracja zmiennej globalnej może wystąpić na głównym poziomie arkusza. Zmienna jest widoczna we wszystkich szablonach oraz deklaracjach innych zmiennych (i parametrów) globalnych. Cykliczne definicje są błędami. Wartość zmiennej globalnej jest obliczana tylko raz, ale może zależeć od parametrów lub treści dokumentu.
Wartość zmiennej można podać na dwa sposoby:
select
, wówczas przypisywana jest obliczona wartość wyrażenia,
variable
, która jest interpretowana tak jak fragment
szablonu, a wynik tej interpretacji jest przypisywany na zmienną (jako węzeł dokumentu z odpowiednimi dziećmi,
chyba że podano atrybut as
).
W XSLT 1.0 istnieje różnica między wartością wyrażenia z select
a wartością uzyskaną
w wyniku interpretacji wnętrza variable
. Ta druga jest typu result tree fragment
i nie można już na niej wykonywać takich operacji jak for-each
czy apply-templates
.
To rozróżnienie nie występuje w XSLT 2.0, dzięki temu możliwe jest to.
<xsl:variable name="ile-elementow" select="count(//element())"/> <xsl:variable name="tekst"> <p>Dokument ma <xsl:value-of select="$ile-elementow"/> elementów.</p> </xsl:variable> <xsl:template match="/"> <html> <body> <xsl:sequence select="$tekst"/> </body> </html> </xsl:template>
Poprawnie
<xsl:template match="costam"> <xsl:variable name="jaki_x"> <xsl:choose> <xsl:when test="$x > 0">dodatni</xsl:when> <xsl:when test="$x = 0">równy zero</xsl:when> <xsl:when test="$x < 0">ujemny</xsl:when> </xsl:choose> </xsl:variable> ...<xsl:value-of select="$jaki_x"/>... </xsl:template>
Niepoprawnie
<xsl:template match="costam"> <xsl:choose> <xsl:when test="$x > 0"> <xsl:variable name="jaki_x">dodatni</xsl:variable> </xsl:when> <xsl:when test="$x = 0"> <xsl:variable name="jaki_x">równy zero</xsl:variable> </xsl:when> <xsl:when test="$x < 0"> <xsl:variable name="jaki_x">ujemny</xsl:variable> </xsl:when> </xsl:choose> ...<xsl:value-of select="$jaki_x"/>... </xsl:template>
Chyba nie o to chodzi...
<xsl:template match="costam"> <xsl:variable name="zmienna" select="'wartość domyślna'"/> <xsl:if test="$x = 1255"> <xsl:variable name="zmienna" select="'zachodzi szczególny przypadek'"/> </xsl:if> ...<xsl:value-of select="$zmienna"/>... </xsl:template>
Parametry arkusza (globalne) są zadeklarowane w elementach param
na głównym poziomie arkusza.
Ich wartość można dostarczyć z zewnątrz przed wykonaniem przekształcenia – procesor XSLT powinien to umożliwiać.
Parametry mogą mieć wartość domyślną, którą podaje się tak samo jak wartość zmiennych. Widoczność parametrów arkusza jest taka jak zmiennych globalnych.
W XSLT 2.0 w atrybucie as
można podać typ parametru, a w atrybucie required
powiedzieć
czy parametr jest obowiązkowy.
Sparametryzować można także pojedyncze szablony, robi się to za pomocą elementów param
umieszczonych na początku treści szablonu. Tak samo jak dla parametrów globalnych można podać
wartość domyślną, typ i obowiązkowość.
Do przekazania wartości parametrów do szablonu służą elementy with-param
umieszczone
wewnątrz apply-templates
lub call-template
. Wartość określa się
tak samo jak wartość zmiennych lub domyślną wartość parametrów.
Szablony nazwane wraz z parametrami mogą działać podobnie jak funkcje i pozwalają na „programowanie”.
Pliki: arkusz, dokument wejściowy.
<xsl:template name="silnia"> <xsl:param name="n"/> <xsl:param name="res" select="1"/> <xsl:choose> <xsl:when test="$n > 1"> <xsl:call-template name="silnia"> <xsl:with-param name="n" select="$n - 1"/> <xsl:with-param name="res" select="$n * $res"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$res"/> </xsl:otherwise> </xsl:choose> </xsl:template>
Napisz i przetestuj szablon nazwany repeat
o parametrach value
i n
,
wstawiający n
razy wartość parametru value
.
Atrybut as
parametrów i zmiennych, a także funkcji (o czym w dalszej części zajęć) służy
do określania typu zmiennej parametru lub wyniku funkcji.
Zgodnie z modelem danych XPath 2.0, dopuszczalne są typy atomowe z XML Schema oraz kilka dodatkowych. Jednak wartością może być nie tylko wartość atomowa, ale także węzeł lub sekwencja wartości atomowych i węzłów. Do zapisywania takich skomplikowanych typów służy specjalna składnia.
Pozwala ona na zgrubne określenie długości sekwencji (0, 1, co najmniej 0 i co najmniej 1) oraz na określenie typu dla jej elementów (jednakowego dla wszystkich). Oto kilka przykładów:
xs:date
– pojedyncza wartość wbudowanego typu daty,xs:date?
– pojedyncza data lub sekwencja pusta,xs:date*
– sekwencja dowolnej liczby dat (także pusta),xs:date+
– niepusta sekwencja dat,node()
– dowolny węzeł,element()
– węzeł elementu (analogicznie dla attribute()
,
text()
, comment()
, processing-instruction()
, document-node()
),element(osoba)
– węzeł elementu osoba
,element(*, Osoba)
– węzeł elementu o dowolnej nazwie i typie Osoba
,item()+
– niepusta sekwencja dowolnych elementów (items,
nie elements :)).Dodaj do parametrów specyfikacje mówiące, że:
value
jest obowiązkowy,n
ma domyślną wartość 1
,value
jest dowolną sekwencją dowolnych węzłów,n
jest pojedynczą nieujemną liczbą całkowitą.
Dla takiego dokumentu napisz arkusz, który zamienia rozdziały na akapity (p
)
a ich tytuły przedstawia jako nagłówki (h1
, h2
, itd.).
Tytuł rozdziału zagnieżdżonego na poziomie N jest reprezentowany jako nagłówek stopnia min(N, 5)
,
a element p
ma atrybut class
równy levelN
.
Zadanie można rozwiązać na co najmniej dwa sposoby: przekazując parametry i licząc poziom zagnieżdżenia ścieżkami. Wypróbujmy oba.
W XSLT 2.0 można definiować własne funkcje, których następnie można używać w wyrażeniach XPath arkusza.
Funkcje definiuje się w elementach function
na głównym poziomie arkusza. Opcjonalny parametr
as
opisuje typ wyniku, opcjonalny parametr override
mówi czy chcemy nadpisać
ewnetualnie istniejącą definicję funkcji o takiej samej nazwie, liczbie parametrów i priorytecie (domyślnie tak).
Zawartość elementu function
jest taka jak szablonu: najpierw elementy param
określające parametry funkcji, a następnie konstruktory i instrukcje tworzące wynik funkcji.
Plik: silnia-fun.xsl.
<xsl:function name="loc:silnia"> <xsl:param name="n"/> <xsl:sequence select="if($n <= 1) then 1 else $n * loc:silnia($n - 1)"/> </xsl:function>
Napisz i przetestuj funkcję repeat
o parametrach value
i n
,
zwracającą w wyniku sekwencję n
razy skopiowanej wartości parametru value
.
Dodaj do parametrów i funkcji specyfikacje mówiące, że:
value
jest dowolną sekwencją dowolnych węzłów,n
jest pojedynczą nieujemną liczbą całkowitą,Dlaczego nie specyfikujemy tu obowiązkowości ani nie podajemy wartości domyślnej?
Przy okazji - to ilustracja skróconej składni dla arkuszy XSLT.
<table xsl:version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <tr> <th>Position</th> <th>Country</th> <th>City List</th> <th>Population</th> </tr> <xsl:for-each-group select="cities/city" group-by="@country"> <tr> <td><xsl:value-of select="position()"/></td> <td><xsl:value-of select="@country"/></td> <td> <xsl:value-of select="current-group()/@name" separator=", "/> </td> <td><xsl:value-of select="sum(current-group()/@pop)"/></td> </tr> </xsl:for-each-group> </table>
sklep2.xml – napisz arkusz, który wypisuje kolejno wszystkie kategorie i dla każdej kategorii wszystkie towary z tej kategorii (bez powtórzeń!).
Zrób to w wersji XSLT 1.0 używając odpowiednich ścieżek i warunków,
oraz w wersji XSLT 2.0, używając konstrukcji for-each-group
.
Tryby przetwarzania (modes) pozwalają na przetwarzanie tego samego
węzła na wiele różnych sposobów. Podczas przetwarzania zawsze obowiązuje jeden tryb bieżący,
który może się zmieniać przy wywołaniach apply-templates
. W wywołaniu mówimy, jaki tryb
ma zostać użyty. W szablonach mówimy, w jakich trybach dany szablon ma być używany.
Szablon może posiadać atrybut mode
, w którym określa się
w jakich trybach szablon ma być używany. Wartością atrybutu jest lista rozdzielonych spacjami tokenów, którymi mogą być:
#default
– oznacza tryb domyślny,#all
– oznacza, że szablon działa we wszystkich trybach.
Instrukcja apply-templates
może posiadać atrybut mode
,
którego wartością jest jeden token:
#default
– oznacza tryb domyślny,#current
– oznacza, że stosujemy tryb bieżący.Podczas dopasowywania szablonów używane są tylko szablony właściwe dla wskazanego trybu.
Istnieje jeden tryb domyślny, który nie posiada nazwy,
i jest trybem aktywnym na początku przetwarzania. Szablon bez atrybutu mode
działa (tylko) w trybie domyślnym, a instrukcja apply-templates
bez atrybutu
mode
uruchamia przetwarzanie w trybie domyślnym. Tryb domyślny jest także
uruchamiany zawsze podczas przetwarzania ciała funkcji (ale nie jest tak już dla ciała
zmiennych, parametrów ani szablonów nazwanych – w tych przypadkach po prostu dalej obowiązuje tryb bieżący).