XML – ćwiczenia 10: XSLT (2)

Szablony nazwane

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.

Przykład 1.

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>

Parametry i zmienne

XSLT pozwala na deklarowanie parametrów i zmiennych, których następnie można używać w wyrażeniach XPath, np.: $nazwa.

Zmienne

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:

  • poprzez wyrażenie XPath w atrybucie select, wówczas przypisywana jest obliczona wartość wyrażenia,
  • poprzez zawartość elementu 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.

Przykład 2. Zmienne globalne

  <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>

Przykład 3. Zmienne lokalne

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 &lt; 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 &lt; 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

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.

Parametry szablonów

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”.

Przykład 4. Silnia (szablon nazwany)

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 &gt; 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>

Zadanie 1.

Napisz i przetestuj szablon nazwany repeat o parametrach value i n, wstawiający n razy wartość parametru value.

Specyfikowanie typów

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 :)).

Zadanie 2.

Dodaj do parametrów specyfikacje mówiące, że:

  • parametr value jest obowiązkowy,
  • parametr n ma domyślną wartość 1,
  • parametr value jest dowolną sekwencją dowolnych węzłów,
  • parametr n jest pojedynczą nieujemną liczbą całkowitą.

Zadanie 3.

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.

Definiowanie własnych funkcji

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.

Przykład 5. Silnia (funkcja)

Plik: silnia-fun.xsl.

  <xsl:function name="loc:silnia">
    <xsl:param name="n"/>
    <xsl:sequence select="if($n &lt;= 1) then 1 else $n * loc:silnia($n - 1)"/>
  </xsl:function>

Zadanie 4.

Napisz i przetestuj funkcję repeat o parametrach value i n, zwracającą w wyniku sekwencję n razy skopiowanej wartości parametru value.

Zadanie 5.

Dodaj do parametrów i funkcji specyfikacje mówiące, że:

  • parametr value jest dowolną sekwencją dowolnych węzłów,
  • parametr n jest pojedynczą nieujemną liczbą całkowitą,
  • wynikiem funkcji jest dowolna sekwencja dowolnych węzłów.

Zadanie 6.

Dlaczego nie specyfikujemy tu obowiązkowości ani nie podajemy wartości domyślnej?

Grupowanie

Przykład 6. Przykład z rekomendacji

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>

Zadanie 7.

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

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ć:

Instrukcja apply-templates może posiadać atrybut mode, którego wartością jest jeden token:

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).


Valid XHTML 1.1Valid CSS