XML – ćwiczenia 9: 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 name="opisz-element">
  <p>Element o nazwie <xsl:value-of select="name()"/>.</p>
</xsl:template>

<xsl:template match="/">
  <html>
    <body>
      <h1>Wszystkie elementy:</h1>
      <xsl:for-each select="//*">
        <xsl:call-template name="opisz-element"/>
      </xsl:for-each>
      <h1>Elementy poziomu głównego:</h1>
      <xsl:for-each select="/*/*">
        <xsl:call-template name="opisz-element"/>
      </xsl:for-each>
    </body>
  </html>
</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 konstruktorów sekwencji. 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, 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 konstruktor sekwencji, a wynik tej interpretacji jest przypisywany na zmienną (jako sekwencja węzłów dokumentu, 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

<xsl:template match="konto">
  <xsl:variable name="jakie">}
    <xsl:choose>
      <xsl:when test="saldo &gt; 0">dodatnie</xsl:when>
      <xsl:when test="saldo &lt; 0">ujemne</xsl:when>
      <xsl:otherwise>równe zero</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  Saldo konta jest <xsl:value-of select="$jakie"/>.
</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 procedury 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.

Instrukcje do tworzenia węzłów

Do tworzenia poszczególnych węzłów dokumentu wynikowego można użyć odpowiednich instrukcji XSLT:

Przykład 5.

<xsl:element name="elem-{$sufix}">
  <xsl:attribute name="atryb">wartość atrybutu</xsl:attribute>
  <xsl:text>Zawartość tekstowa</xsl:text>
</xsl:element>
<xsl:comment>To będzie komentarz</xsl:comment>
<xsl:processing-instruction target="xml-stylesheet">type="text/css" href="styl.css"</xsl:processing-instruction>

Zadanie 2. Zadanie rezerwowe, gdyby na końcu zajęć został czas

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 6. 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 3.

Napisz i przetestuj funkcję count-elems(name, node) zwracającą liczbę wystąpień elementów o nazwie name w poddrzewie o korzeniu node (łącznie z node).

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

Do poprzedniej funkcji dodaj specyfikację typów argumentów i wyniku.

Grupowanie

Przykład 7. 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 5.

sklep2.xml – napisz arkusz, który wypisuje (po jednym razie) wszystkie kategorie i dla każdej kategorii średnią cenę oraz wszystkie towary z tej kategorii.

Wykonaj w wersji XSLT 2.0, używając konstrukcji for-each-group. Można także spróbować w wersji 1.0.


Valid XHTML 1.1Valid CSS