DTD -- Definice typu dokumentu pod lupou

Jiří Kosek ml.

Vztah DTD k dokumentu

V úvodním článku o XML jsme si řekli, že XML-dokument může vyhovovat určitému typu dokumentu. Definice typu dokumentu (DTD) přitom říká, které elementy a atributy můžeme v dokumentu použít. Navíc je zde definováno, v jakých vzájemných vztazích mohou být jednotlivé elementy použity. DTD je tedy užitečný nástroj, který nám umožní hlídat, zda mají naše dokumenty správnou strukturu. Ve světě se používá mnoho DTD, které vyhovují různým požadavkům. Mezi jedno z nejznámějších patří například DocBook, které definuje elementy a atributy vhodné pro značkování technické dokumentace.

Tím, že naše dokumenty založíme na určitému DTD, získáme hned dvě výhody. Jednak můžeme pomocí parseru kontrolovat, zda má náš dokument správnou strukturu. Druhá výhoda je patrná při použití standardních DTD jako HTML nebo DocBook -- k dispozici budeme mít mnoho užitečných a jednoúčelových nástrojů navrhnutých pro konkrétní DTD. Například není problém pro DocBook sehnat definici stylů vhodných pro formátování dokumentace či programy, které umí dokumenty DocBook konvertovat do HTML a dalších formátů.

DTD se k dokumentu přidává pomocí deklarace typu dokumentu (DOCTYPE), která je umístěna na začátku dokumentu ihned za XML deklarací. Nejčastěji je DTD uloženo v samostatném souboru, aby mohlo být využíváno v mnoha dokumentech. V tomto případě má deklarace tvar:

<!DOCTYPE kořenový_element SYSTEM "URL">

URL přitom udává adresu nebo jméno souboru, ve kterém je uloženo DTD. Kořenový element je jméno elementu, který je v DTD definovaný jako kořenový -- tj. obsahuje všechny další elementy dokumentu.

Pro některá běžně používaná a standardizovaná DTD je zbytečné, aby si parser a další aplikace četly DTD vždy ze sítě. Mnohem logičtější by bylo, aby v systému byla přítomna lokální kopie souborů s DTD. To je v XML možné pomocí takzvaných veřejných identifikátorů. K označení DTD použijeme nějaký textový řetězec. XML-aplikace pak ve svém konfiguračním souboru zjistí, ve kterém souboru je uloženo příslušné DTD. Místo slova SYSTEM nyní použijeme výraz PUBLIC, ze kterým uvedeme identifikátor DTD. Nakonec však stejně musíme připojit URL, které ukazuje na soubor s DTD, aby mohla aplikace DTD získat i v případě, že nerozpozná veřejný identifikátor. Deklarace typu dokumentu pak může dopadnout třeba takto:

<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "docbook.dtd">	 

DTD můžeme umístit i přímo do dokumentu pomocí následujícího zápisu.

<!DOCTYPE kniha [
   ... DTD ...
]>

Umístění DTD přímo do dokumentu není příliš časté, protože pak ztrácíme možnost sdílení jednoho DTD mezi několika dokumenty. Užitečnější je však možnost zkombinovat obě předešlé metody a externí DTD upravit pomocí lokálně uvedených definic. Lokální část DTD se zpracovává ještě před tou externí a může tedy změnit některé definice uložené v externím DTD.

<!DOCTYPE kniha SYSTEM "kniha.dtd" [
   ... definice, které změní nebo rozšíří kniha.dtd ...
]>

Jak vypadá DTD

Když víme, jak připojit DTD k dokumentu, můžeme se podívat na to, jak samotné DTD vypadá. DTD obsahuje deklarace čtyř typů:

Deklarace elementů

Deklarace nového elementu je velice jednoduchá. Má následující tvar:

<!ELEMENT název_elementu obsah_elementu>

Název elementu musí začínat písmenem. Další znaky názvu mohou obsahovat písmena, číslice a některé speciální znaky jako '.', '-', '_' a ':'. Délka jména není nikterak omezená. Narozdíl od HTML je důležitá velikost písmen. Následující dva řádky deklarují dva různé elementy kapitola a Kapitola.

<!ELEMENT kapitola ...>
<!ELEMENT Kapitola ...>

Nejzajímavější je však poslední část deklarace elementu, která definuje, co může element obsahovat. Nejjednodušší je prázdný element, který nemůže obsahovat žádné další elementy nebo text. Příkladem takového elementu mohou být například elementy br a hr, které důvěrně známe z HTML. Jejich deklarace by vypadala následovně:

<!ELEMENT br EMPTY>
<!ELEMENT hr EMPTY>

Právě klíčové slovo EMPTY určuje, že element nesmí nic obsahovat. V dokumentu pak musíme psát buď <br></br>, nebo zkráceně <br/>. Není však možný zápis <br>, protože by parser zcela marně hledal ukončovací tag </br>.

Kromě výše zmíněných případů se prázdné elementy používají například pro vkládání obrázků. Pomocí atributů elementu pak určíme soubor, ve kterém je obrázek uložen.

Protipólem k EMPTY je ANY. Toto klíčové slovo nám zajistí, že element může obsahovat libovolné další elementy a text. ANY se v praxi moc často nevyužívá, protože pro potřeby většiny aplikací příliš uvolňuje strukturu dokumentu. Využití nalezne například při návrhu a ladění DTD, kdy nechceme najednou napsat celé DTD.

<!ELEMENT cokoliv ANY>

Většinou máme na vnořené elementy mnohem striktnější požadavky a s EMPTY a ANY nevystačíme. V tomto případě pak použijeme tzv. modelovou skupinu (model group). Modelová skupina se používá pro definici elementů, které obsahují další elementy nebo mají smíšený obsah (obsahují již přímo text a elementy).

Modelová skupina je vždy uzavřena do kulatých závorek a obsahuje alespoň jedno slovo. Tímto slovem nejčastěji bývá jméno elementu, který může být obsažen v právě definovaném elementu. Vnořené elementy můžeme navzájem kombinovat pomocí oddělovačů ',' a '|'. Elementy oddělené čárkou musí následovat v pořadí, v jakém jsou uvedeny. Pokud má tedy element html obsahovat záhlaví (head) a tělo (body), použijeme deklaraci:

<!ELEMENT html (head, body)>

Pokud naopak elementy oddělíme znakem '|' může být obsažen pouze jeden z nich. Příklad: potomek může být dcera nebo syn. V DTD to vyjádříme takto:

<!ELEMENT potomek (dcera | syn)>

Pomocí závorek můžeme obě dvě varianty navzájem kombinovat. Pokud má nějaký element obsahovat elementy a, b a za nimi buď c nebo d, použijeme modelovou skupinu (a, b, (c | d)).

Kromě pořadí elementů musíme určit jejich počet, zda jsou povinné či zda se mohou opakovat. Pokud v modelové skupině uvedeme pouze jméno elementu, musí být element přítomen právě jednou. Pokud je však výskyt elementu nepovinný, uvedeme za jeho jméno znak '?'. Pokud například článek obsahuje vždy název, ale autora obsahovat nemusí, můžeme použít následující deklaraci:

<!ELEMENT clanek (nazev, autor?)>

Další obvyklou situací je, že nějaký element se může opakovat, ale musí být přítomen alespoň jednou. Například kniha se skládá z několika kapitol, ale musí obsahovat alespoň jednu kapitolu:

<!ELEMENT kniha (kapitola+)>

Vraťme se k předchozímu příkladu a předkládejme, že článek může mít více autorů a nemusí mít autora žádného. V tomto případě s výhodou využijeme znak '*', který indikuje libovolný počet opakování.

<!ELEMENT clanek (nazev, autor*)>

Vše můžeme podle potřeby kombinovat. Pokud chceme vyjádřit, že seznam obsahuje alespoň dvě položky, můžeme použít modelovou skupinu (polozka, polozka+).

Indikátor počtu výskytů můžeme připojit i za modelovou skupinu. Například zápis (a, b)? říká, že se elementy a a b buď musí vyskytovat v daném pořadí, nebo nesmí být použity.

Speciální pozornost si zaslouží případ, kdy je obsahem elementu již samotný text. To vyjádříme pomocí slova #PCDATA. Pokud by například element em obsahoval již pouze text, a ne další elementy, použili bychom pro jeho deklaraci zápis:

<!ELEMENT em (#PCDATA)>

Pokud může element obsahovat jak text tak elementy říkáme, že má smíšený obsah. V tomto případě musí mít deklarace jeho obsahu speciální tvar. #PCDATA musí být uvedeno ve skupině jako první, skupina musí být spojena pomocí operátoru '|' a musí být volitelně opakovatelná (*). Například

<!ELEMENT em  (#PCDATA, sub, sup)*>
<!ELEMENT sub (#PCDATA)>
<!ELEMENT sup (#PCDATA)>

Dokument pak může obsahovat následující text vyhovující DTD:

<em>Pozor na líh - C<sub>2</sub>H<sub>5</sub>OH.</em>

Máme za sebou úvod do tvorby té části DTD, která definuje použitelné elementy. Příště se podíváme na to, jak k elementům přidat atributy. Poté si již ukážeme, jak můžeme pomocí parseru kontrolovat správnost našich dokumentů.

Veřejné identifikátory

Použití veřejných identifikátorů má mnohé výhody. Aby jsme je mohli použít, musíme mít na svém počítači lokální kopie všech souborů DTD, které používáme. V souboru catalog pak musíme mít k dispozici mapování mezi veřejnými identifikátory a soubory. Máme-li například DTD uložena v adresáři c:\dtd, budeme mít pro DocBook v katalogovém souboru řádek

PUBLIC "-//OASIS//DTD DocBook V3.1//EN" "c:\dtd\docbook.dtd"

Aby všechny aplikace katalogový soubor našly, musíme jeho umístění uložit do proměnné prostředí SGML_CATALOG_FILES.

Samotný význam jednotlivých části veřejného identifikátoru vychází z SGML. První znak '-' znamená, že identifikátor není zaregistrován. Znamená to, že není zaručena jeho celosvětová jednoznačnost. Pro registrované identifikátory se na tomto místě používá znak '+'. Pokud je vlastníkem organizace ISO, uvádí se na začátku řetězec ISO 8879:1986.

Další část identifikátoru označuje organizaci nebo osobu, která je vlastníkem souboru označeného veřejným identifikátorem. Například pro HTML má identifikátor tvar

-//W3C//DTD HTML 4.0 Transitional//EN

Na první pohled vidíme, že vlastníkem je konsorcium W3C. U DocBooku je to zase firma Oasis.

Za dalšími dvěma lomítky následuje určení typu souboru. V našem případě tam bude vždy DTD, protože se odkazujeme na soubor s DTD. Poté následuje textové označení dokumentu -- například DocBook V3.1 pro verzi 3.1 systému DocBook. Za posledními dvěma lomítky je kód jazyka, ve kterém je DTD zapsáno. Nejčastěji se setkáme s kódem EN, který odpovídá angličtině.

© Jiří Kosek 1999