Kompletní průvodce XSLT – zpracování více stylů a dokumentů

21. července 2004

Jak každý programátor ví, je třeba pokud možno vždy bránit duplicitám, a to jak kódu, tak v datech. Obvykle potřebujeme některé šablony a fragmenty vstupních dat používat v několika různých stylech, v čemž nám vychází jazyk XSLT vstříc. Pojďme se tedy podívat, jaké prostředky nám vlastně nabízí.

Připojování dalších stylů

Stylový předpis (stylesheet) je v podstatě adekvátní zdrojovému kódu běžných programovacích jazyků. Šablony můžeme přirovnat k jednotlivým funkcím nebo metodám. Každý jazyk ale také nabízí seskupení těchto funkcí, metod nebo celých tříd do nějakých vyšších logických celků, což je obvykle jeden soubor, a jednotlivé soubory mohou mít mezi sebou vazby. V ASP či PHP máme k dispozici instrukci include a v XSLT je situace velmi obdobná.

<xsl:include />

Každý stylesheet může mít připojen další stylesheet s definicemi dalších šablon. To je možné díky instrukci xsl:include s povinným parametrem „href“, obsahujícím URI vkládaného stylu. Tato URI může být i relativní, v takovém případě se vychází z bázové URI dokumentu. Instrukci lze zapsat pouze do nejvyšší úrovně stylu, tedy přímo do těla elementu xsl:stylesheet. Procesor při jejím výskytu načte odkazovaný stylesheet, a veškerý obsah značky xsl:stylesheet vloží do právě zpracovávaného stylesheetu místo instrukce xsl:include.

<xsl:stylesheet version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <xsl:include href=“format.xsl“/>
  <xsl:include href=“tables.xsl“/>
  …
</xsl:stylesheet>

Vkládaný stylesheet může rovněž obsahovat další vkládané stylesheety, avšak hierarchie vkládání se nesmí dostat do rekurze, kdy stylesheet přímo či nepřímo obsahuje sám sebe – to je považováno za chybu. Vkládání jednoho stylu vícekrát než jednou je rovněž chybou, protože to způsobuje duplicitu šablon, které vkládaný styl obsahuje. Je třeba dávat pozor hlavně v případě víceúrovňového vkládání, kdy styl sice obsahuje dva (nebo více) různých stylů, avšak každý z nich využívá třetí společný styl. V takové situaci je třeba hierarchii přehodnotit a vymyslet jinou, kde by k takové situaci nedocházelo.

Vkládaný styl může používat zjednodušenou syntaxi (styl neobsahuje značky xsl:stylesheet ani xsl:template, celý dokument je uvažován jako jediná šablona pro kořenový element vstupního dokumentu). Příklad takového stylesheetu:

<html xsl:version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <head>
    <title><xsl:value-of select=“article/Name“ /></title>
  </head>
  <body>
    …
  </body>
</html>

<xsl:import />

Další možností využití jiného stylesheetu je jeho importování pomocí instrukce xsl:import opět s parametrem „href“. Tato instrukce má podobný význam jako xsl:include, avšak duplicitní definice jsou zpracovány takovým způsobem, že definice v následujícím stylu přepisuje definici ve stylu předcházejícím. Dále platí, že vyšší prioritu mají šablony na vyšší úrovní hierarchie, tedy přímo zapsané definice mají přednost před importovanými. Všechny instrukce xsl:import ve všech stylech musí být uvedeny dříve než ostatní XSLT elementy včetně xsl:include – ty mohou být jinak kdekoli (na nejvyšší úrovni).

Opět zde platí, že žádný styl nesmí importovat přímo či nepřímo sám sebe. Na druhou stranu nevadí, když se jeden styl v hierarchii vyskytuje vícekrát. Definice v každém dalším výskytu prostě překryjí definice ze stejného stylesheetu předchozího.

<xsl:apply-imports />

Každá šablona určená podmínkou (parametrem „match“) ve stylu může předat řízení šabloně z importovaného stylu prostřednictvím instrukce xsl:apply-imports. Přitom se cílová šablona vyhledává podle podmínky z šablony volající. Cílová šablona tedy bude pro aktuální uzel vyvolána v každém případě – pokud uzel vyhověl podmínce jednou, vyhoví i podruhé. To samozřejmě nemůže platit, pokud je instrukce xsl:apply-imports obsažena v xsl:for-each, kdy se mění aktuální uzel, který pak s velkou pravděpodobností vyhovovat nebude. Proto je tato situace považovaná za chybu stylesheetu.

Malý příklad stylesheetu:

<xsl:stylesheet version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <xsl:import href=“item-details.xsl“/>
  …
  <xsl:template match=“item“>
    <h4>item <xsl:value-of select=“@name“ /></h4>
    <p><xsl:apply-imports /></p>
  </xsl:template>
  …
</xsl:stylesheet>

Šablona s podmínkou zobrazuje jednu položku (item) vstupního dokumentu. K zobrazení detailu využívá importovaný styl, ve kterém se nachází šablona se stejnou podmínkou, která má však nízkou prioritu a jinak než instrukcí xsl:apply-imports ji nelze zavolat:

<xsl:stylesheet version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  …
  <xsl:template match=“item“>
    <dl>
      <xsl:for-each select=“*“>
        <dt><xsl:value-of select=“local-name(.)“/></dt>
        <dd><xsl:value-of select=“.“/></dd>
      </xsl:for-each>
    </dl>
  </xsl:template>
  …
</xsl:stylesheet>

Možná si ještě zaslouží vysvětlení smyčka xsl:for-each v šabloně, která prochází podřízené elementy elementu item. V ukázce je každý element parametrem položky a všechny jsou šablonou převedeny na HTML seznam definic. Pro dokreslení fragment vstupního dokumentu:


<item name=“iRiver IMP-400″>
  <Description>CD/MP3 player</Description>
  <Formats>MP3, ASF, WMA, OGG</Formats>
  <Price>low</Price>
</item>

Toto řešení prosím berte pouze jako ukázkové, protože název parametru je vždy omezen tím, jak lze napsat název XML elementu. Ve skutečnosti bychom samozřejmě napsali název parametru například do hodnoty atributu „name“, já jsem chtěl jen při této příležitosti ukázat využití XPath funkce local-name().

Připojování dalších vstupních dokumentů

Někdy nám data ve vstupním dokumentu nestačí a potřebujeme využít nějaký další zdroj. I na tento požadavek konsorcium W3C myslelo a nabídlo nám následující řešení.

document()

Tato funkce zpřístupňuje nějaký XML dokument, jehož URI je dáno prvním parametrem této funkce. Pokud je tímto parametrem seznam uzlů (node-set), je každý uzel převeden na řetězec představující URI dokumentu k načtení. URI dokumentů mohou být relativní, jako výchozí URI může posloužit URI uzlu předaného druhým parametrem funkce document(). Pokud tento parametr není využit, základem se stane URI XSLT instrukce, která funkci document() vyvolala – tato URI se většinou shoduje s URI celého stylesheetu, tedy umístění, ze kterého byl stylesheet načten.

Bez zajímavosti není prázdné URI při volání document(''), kdy se jako bázová URI (jak již bylo řečeno) použije bázová URI stylesheetu a, protože relativní URI ji nijak neovlivní, dojde k načtení samotného stylesheetu a vrácení jako XML uzlu ve výsledku funkce, což se někdy může hodit.

Výsledkem je seznam uzlů, zahrnující obsah všech načtených XML dokumentů, se kterým můžeme dále pracovat. Pokud při načítání dojde k chybě (nepodporované URI schéma, načtený dokument není validní XML a podobně), procesor může skončit s výjimkou nebo funkce document() vrátí prázdný node-set.

Myslím, že malá ukázka opět neuškodí. Bude se jednat o podpůrný styl použitelný z jiných šablon, jehož pojmenovaná šablona SelectCountry bude generovat HTML element select se seznamem zemí. A protože jsme již získali mnoho znalostí, zopakujeme si je co nejvíce (parametry, proměnné, iterace, podmínky, generování atributů a další). Vstupní dokument bude jednoduchý:

<Countries>
  <Coutry code=“CZ“>Česká republika</Country>
  <Coutry code=“SK“>Slovensko</Country>
  <Coutry code=“PL“>Polsko</Country>
</Countries>

Stylesheet už je o něco složitější:

<xsl:stylesheet version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <xsl:variable name=“countries“
      select=“document(‚countries.xml‘)“ />
  <xsl:template name=“SelectCountry“>
    <xsl:param name=“Name“ select=“‚Country'“ />
    <xsl:param name=“Class“ />
    <xsl:param name=“Selected“ select=“““ />
    <select name=“{$Name}“>
      <xsl:if test=“$Class“>
        <xsl:attribute name=“Class“>
          <xsl:value-of select=“$Class“ />
        </xsl:attribute>
      </xsl:if>
      <option value=““>— vyberte zemi —</option>
      <xsl:for-each select=“$countries/Countries/Country“>
        <option value=“{@code}“>
          <xsl:if test=“@code = $Selected“>
            <xsl:attribute
                name=“selected“>selected</xsl:attribute>
          </xsl:if>
          <xsl:value-of select=“.“ />
        </option>
      </xsl:for-each>
    </select>
  </xsl:template>
</xsl:stylesheet>

Použití takového stylesheetu je velice snadné, vývojář už se o nějaké načítání seznamu ze souboru a generování elementu select nemusí starat:

<xsl:stylesheet version=“1.0″
    xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>
  <xsl:import href=“countries.xsl“ />
  <xsl:template match=“/“>
    …
    <xsl:call-template name=“SelectCountry“>
      <xsl:with-param name=“Name“ select=“‚UserCountry'“/>
      <xsl:with-param name=“Selected“ select=“‚CZ'“/>
    </xsl:call-template>
    …
  </xsl:template>
</xsl:stylesheet>

Odkazy, zdroje

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *