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

W XSLT 2.0 można dodać atrybut as, w którym podaje się typ zmiennej.

Trochę przykładów z rekomendacji.

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>

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

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

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.

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?


Valid XHTML 1.1Valid CSS