Kompletní průvodce XSLT – jmenné prostory

2. srpna 2004

Dosud jsme pracovali s dokumenty bez definovaného jmenného prostoru a naše výsledné dokumenty na tom byly stejně. S tím si ovšem v praxi nevystačíme, takže brzy narazíme na nutnost tuto problematiku řešit. Přitom však mohou nastat určité komplikace, které si ukážeme a vyřešíme v tomto článku. Pro ukázku nám poslouží jeden z nejpopulárnějších XML formátů – XHTML.

Úvod do jmenných prostorů

Jmenný prostor (namespace) je jakási pomyslná množina jmen, které jsou si významově blízké. Jmenný prostor je určen URI identifikátorem, který by měl být v celosvětovém měřítku jedinečný. Například všechny instrukce XSLT procesoru patří do společného jmenného prostoru http://www.w3.org/1999/XSL/Transform, nebo všechny XHTML elementy patří do prostoru http://www.w3.org/1999/xhtml. Touto URI je tedy určen význam XML elementů či atributů (toto je XSLT instrukce, tohle zase XHTML značka) a kdokoli, kdo s daným XML dokumentem pracuje, by je měl takto chápat. Pokud aplikace jmenný prostor nezná, měla by dotyčné elementy či atributy ignorovat.

Vždy deklarujeme jmennému prostoru nějaký prefix, který potom uvedeme před samotný název elementu nebo atributu. Každý název má totiž prefix, dokonce i když není vidět. V takovém případě totiž značka používá prefix, který má hodnotu prázdného řetězce (empty string). Jak jste si určitě již všimli, prefix se od názvu odděluje dvojtečkou (například xsl:template).

Některé prefixy jsou již standardně předdefinované, malý přehled ukazuje následující tabulka:

Prefix URI a význam
„xml“ „http://www.w3.org/XML/1998/namespace“
Jmenný prostor pro atributy všeobecně popisující daný XML dokument nebo jeho část, například jazyková verze je definována atributem xml:lang.
„xmlns“ „http://www.w3.org/2000/xmlns“
Jmenný prostor sloužící k definici prefixů jmenných prostorů. Příklad viz níže.
„“ „“
Výchozí jmenný prostor pro elementy (a atributy) bez prefixu. Pokud jej nepřepíšete, bude URI jmenného prostoru prázdné.

Pokud v některém místě dokumentu deklarujeme nějaký prefix, je tento prefix viditelný již v aktuálním elementu a také ve všech jeho potomcích. Elementy bez prefixu použijí jmenný prostor s prázdným prefixem a atributy bez prefixu použijí jmenný prostor příslušného elementu. Jak tedy prefix definovat?

<html xmlns=“http://www.w3.org/1999/xhtml“>

Tímto zápisem jsme definovali URI jmenného prostoru s prázdným prefixem a protože samotný element html tento prefix používá, automaticky spadá do tohoto jmenného prostoru. Spadají do něj i všichni následující potomci, tedy elementy head, title, body a další, takže u nich již nemusíme jmenný prostor nijak specifikovat.

<xsl:stylesheet xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“>

Zde jsme definovali prefix „xsl“ a ihned jej využili v samotném elementu stylesheet. Prefix je opět viditelný pro všechny potomky, takže kdekoli dále můžeme zapsat například element xsl:template. Pokud pak k této značce přidáme atribut „match“, bude tento atribut automaticky rovněž v daném jmenném prostoru, takže před jeho název již prefix psát nemusíme.

Tvoříme validní XHTML dokument

Nejlépe se naučíme práci se jmennými prostory v XSLT na nějaké ukázce, což bude v našem případě generování XHTML dokumentu se všemi potřebnými hlavičkami, aby výsledek dobře vypadal. Prvním prvkem XML dokumentu je XML deklarace. Její existenci (a mnoho dalších vlastností výstupního dokumentu) můžeme nastavit pomocí XSLT instrukce, o které jsme zatím nemluvili.

<xsl:output />

Je to další top-level instrukce, tedy zapisuje se do stylesheetu jako přímý potomek elementu xsl:stylesheet. Jeho úkolem je bližší upřesnění, jak má výstup XSLT procesoru vypadat.

První parametr, který nás zajímá, je method, který specifikuje formát výstupu procesoru. Přednastavenou výchozí hodnotou je samozřejmě „xml“, dalšími možnostmi jsou „html“ a „text“. Uvedeme-li „html“, procesor generuje výstup odpovídající standardnímu HTML, tedy zapisuje nepárové elementy bez ukončovacího lomítka, zkracuje atributy (například checked) a používá znakové entity. V režimu „text“ se na výstup generují pouze textové uzly. My ponecháme hodnotu parametru v jejím výchozím stavu.

Další související parametr je version, určující verzi výstupního formátu, v našem případě se jedná o verzi „1.0“. Dále si vybereme výstupní kódování textu parametrem encoding, který můžeme nastavit na „utf-8“, což je moje oblíbená znaková sada. A konečně se dostáváme k parametru omit-xml-declaration, který nastavený na „yes“ zabraňuje vygenerování XML deklarace na začátek výstupního dokumentu. To může být pro HTML kodéry důležitý parametr, protože takové „detaily“ mohou ovlivnit režim prohlížeče.

Protože nejen XML deklarací živ je programátor (a HTML prohlížeč), přesuneme se k deklaraci typu dokumentu. Pořád zůstaneme u jeho instrukce xsl:output, podíváme se ale na zoubek dalším parametrům, doctype-public a doctype-system. Pro naše potřeby se hodí hodnoty -//W3C//DTD XHTML 1.1//EN a http://www.w3c.org/TR/xhtml11/DTD/xhtml11.dtd. Říkáme tak celému světu, že náš dokument odpovídá všeobecně uznávanému standardu XHTML 1.1.

Nakonec popřemýšlíme, zda se do zdrojového kódu našeho dokumentu budou dívat jen počítače nebo se tam občas podívá i nějaký šťoural. V druhém případě bychom totiž požadovali, aby byl XHTML kód úhledně zarovnaný s patřičným odsazením elementů dle umístění ve struktuře, což lze zajistit dalším parametrem indent nastaveným na hodnotu „yes“. Pokud je hodnota „no“, elementy jsou na sebe doslova nalepené, díky čemuž se přenáší méně dat.

Deklarace XHTML jmenného prostoru

Nyní se přesuneme ke značce <html>, kde nastavíme pro výstupní dokument příslušný jmenný prostor. Tuto značku máme pravděpodobně umístěnou v šabloně pro zpracování kořenového elementu vstupního dokumentu:

<xsl:stylesheet
  xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“
  version=“1.0″>
  <xsl:output
    method=“xml“ version=“1.0″
    indent=“yes“
    encoding=“utf-8″
    doctype-public=“-//W3C//DTD XHTML 1.1//EN“
    doctype-system=
        „http://www.w3c.org/TR/xhtml11/DTD/xhtml11.dtd“
    />
  <xsl:template match=“/“>
    <html xmlns=“http://www.w3.org/1999/xhtml“>
      …
    </html>
  </xsl:template>
</xsl:stylesheet>

Ale ouha – tohle je špatně! Je velmi svůdné nastavit deklaraci jmenného prostoru tam, kde ji chceme, ale není to ten správný přístup. Víte proč? XSLT procesor totiž tuto deklaraci zpracuje prostě tak, že tento jmenný prostor přiřadí elementu html, kde je definován, a všem následníkům v dané šabloně. Všimněte si, prosím, slov „v dané šabloně“. XSLT stylesheet je stále jen XML dokument a také se tak s ním zachází. A jmenné prostory se dědí jen do podřízených elementů a ne už do dalších šablon. V jiných šablonách se pro značky bez prefixu automaticky použije opět prefix prázdný, jehož jmenný prostor má prázdné URI. Až bude procesor tyto elementy zapisovat na výstup, zjistí, že jejich jmenný prostor neodpovídá jmennému prostoru nadřazeného elementu na výstupu, a přepíše jej. Výsledek může vypadat třeba takto:

<?xml version=“1.0″ encoding=“utf-8″?>
<!DOCTYPE html PUBLIC „-//W3C//DTD XHTML 1.1//EN“
  „http://www.w3c.org/TR/xhtml11/DTD/xhtml11.dtd“>
<html xmlns=“http://www.w3.org/1999/xhtml“>
  <head>
    …
  <head>
  <body>
    …
    <p xmlns=““>
      <b>A tady jsme již ve špatném namespace…</b>
    </p>
    …
  <body>
</html>

No uznejte sami – napadlo by vás při pohledu na to podivné xmlns="", kde se stala chyba? Za takový „přestupek“ by vás jistě šťouralové roznesli na kopytech. Můžeme se tomu ale vyhnout, pokud chybu opravíme. Stačí se zamyslet nad její podstatou. Jmenný prostor pro prázdný prefix "" je děděn z elementu html pouze na jeho potomky, my jej chceme ale přenést i do jiných šablon. Řešení je jednoduché, prostě definici prefixu přeneseme do elementu xsl:stylesheet. A je po problému.

Nakonec ještě drobný detail pro puntičkáře – nastavíme značce html atribut xml:lang s hodnotou „cs“ (pokud se jedná o stránku v českém jazyce). Tento atribut procesor nijak nemění ani nepřepisuje, poctivě jej nechává tam, kde je. Základ našeho finálního stylesheetu generujícího XHTML tedy bude:

<xsl:stylesheet
  xmlns:xsl=“http://www.w3.org/1999/XSL/Transform“
  xmlns=“http://www.w3.org/1999/xhtml“
  version=“1.0″>
  <xsl:output
    method=“xml“ version=“1.0″
    indent=“yes“
    encoding=“utf-8″
    doctype-public=“-//W3C//DTD XHTML 1.1//EN“
    doctype-system=
        „http://www.w3c.org/TR/xhtml11/DTD/xhtml11.dtd“
    />
  <xsl:template match=“/“>
    <html xml:lang=“cs“>
      …
    </html>
  </xsl:template>
</xsl:stylesheet>

Zpracování dokumentů se jmenným prostorem

Chceme-li zpracovávat dokumenty s definovaným jmenným prostorem, musíme vytvořit stejnou definici v našem stylesheetu, nejlépe pod nějakým (neprázdným) prefixem. Například pokud na vstupu budeme mít opět XHTML dokument, definujeme třeba xmlns:html="http://www.w3.org/1999/xhtml". Pak tento prefix musíme poctivě psát do všech XPath výrazů, které tento dokument procházejí, tedy například /html:head/html:body/html:title.

Při zpracování takového dokumentu se nám snadno může stát, že procesor na výstup přenáší definice jmenných prostorů, které tam mít nechceme. Zabráníme tomu jednoduše použitím parametru značky xsl:stylesheet, který se jmenuje exclude-result-prefixes. Do něj zapíšeme prefixy všech jmenných prostorů, po kterých netoužíme, oddělené mezerou.

Generování dokumentů se jmenným prostorem

Chceme-li generovat dokumenty s jedním či několika specifikovanými jmennými prostory, stačí deklarovat prefix a používat jej tak, jak jsme si to ukázali na vytváření XHTML dokumentu výše (sice jsme použili prázdný prefix, to však není podmínkou). Problém však může nastat, když potřebujeme generovat elementy ve jmenném prostoru, který je již využit jinak. Například stylesheetem generujeme nový stylesheet. Jak donutit procesor, aby elementy ve jmenném prostoru náležícím XSLT nepovažoval za XSLT instrukce, které má ihned zpracovat, ale aby je chápal jako elementy patřící do toku výstupního dokumentu? Nejdříve pro tyto elementy vytvoříme nový prostor, třeba xslo, a k němu přiřadíme URI s nějakým vymyšleným názvem. Tento prefix použijeme pro všechny XSLT elementy patřící na výstup. A pak se postaráme o přejmenování tohoto prefixu instrukcí xsl:namespace-alias.

<xsl:namespace-alias />

Tato instrukce umožňuje změnit prefix výstupních elementů. Prvním parametrem je stylesheet-prefix, který udává prefix, jehož se změna týká (v našem případě xslo), a druhý parametr result-prefix určuje nový prefix (xsl). Na výstupu již budou mít tyto elementy správný jmenný prostor.

Rozšíření funkčnosti XSLT procesoru

Většina XSLT procesorů má podporu pro rozšiřující funkce, které ve standardu XSLT nejsou definovány. Nacházejí se ve speciálním jmenném prostoru, který je určen konkrétní implementací XSLT procesoru. Některé procesory také umožňují přidání rozšiřujících funkcionalit přímo programátorem. URI jmenného prostoru pak záleží na programátorovi daného rozšíření.

První možností je definování vlastních elementů. Element se nekopíruje do výstupního dokumentu, ale je vyvolána nějaká speciální funkce, která se může o generování výstupních dat postarat sama. Že se jedná o rozšiřující element musíme určit atributem xsl:extension-element-prefixes, do něhož napíšeme prefix rozšiřujícího elementu nebo „#default“, pokud element prefix nepoužívá. Atribut zapisujeme přímo do rozšiřujícího elementu nebo do elementu xsl:stylesheet.

Zda XSLT procesor dané rozšíření implementuje, lze vyzkoušet XPath funkcí element-available(), jejímž parametrem je řetězec s názvem testované funkce. Pokud se stylesheet pokusí vyvolat rozšiřující element, který není implementován, pokusí se v těle tohoto elementu vyhledat instrukci xsl:fallback a zpracovat její tělo jako šablonu. Pokud tato instrukce není nalezena, procesor vyvolá výjimku a ukončí zpracování stylesheetu.

Jinou možností rozšíření je vlastní XPath funkce. Při jejím volání se nemusí nijak určovat, že se jedná o rozšiřující funkci – je totiž považována za rozšiřující vždy, když XSLT procesor nezná její název. Obvykle se funkce také umisťuje do zvláštního jmenného prostoru, přičemž při jejím volání před její název napíšeme prefix stejně, jako je tomu u elementů a atributů, tedy oddělený dvojtečkou. Například ms:node-set(). Implementaci libovolné funkce lze opět ověřit, a to funkcí function-available(), jejímž parametrem je název funkce jako řetězec.

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 *