Potřebujete pomoci s XML schématy?
Nabízím školení, konzultace, vývoj schémat na zakázku. Nevájte a kontaktujte mne.

Podpořte provoz stránek
Platba probíhá pomocí služby PayPal a je možné platit kartou nebo převodem z vašeho PayPal účtu.

Kapitola 2. DTD

2.1. Deklarace elementů
2.2. Deklarace atributů
2.3. Připojení DTD k dokumentu

Ačkoliv jsou DTD poměrně starou a v mnoha ohledech nedostatečnou technologií, protože nepodporují dnes tolik používané jmenné prostory, i přesto se stále hojně používají. Je to díky výborné podpoře v různých aplikacích a parserech. Ostatní schémové technologie na DTD více či méně navazují, takže je dobré mít alespoň základní znalosti DTD.

Pro rychlé vpravení do problematiky si rovnou ukážeme jednoduchý příklad DTD a dokumentu XML, který mu vyhovuje. Dejme tomu, že chceme vytvořit schéma popisující dokumenty XML vhodné pro přenos informace o jednom zaměstnanci. U zaměstnance nás přitom bude zajímat jeho identifikační číslo, jméno, příjmení, plat a datum narození. Tyto informace můžeme v XML zachytit následujícím způsobem:

<zamestnanec id="101">
  <jmeno>Jan</jmeno>
  <prijmeni>Novák</prijmeni>
  <plat>25000</plat>
  <narozen>1965-12-24</narozen>
</zamestnanec>

Odpovídající schéma tedy definuje, že dokument musí obsahovat element zamestnanec, který obsahuje atribut id a čtyři podelementy jmeno, prijmeni, plat a narozen. Obsah těchto elementů můžeme pak v DTD definovat jako text, jiné datové typy nejsou k dispozici.

d
t
d
<!ELEMENT zamestnanec (jmeno, prijmeni, plat, narozen)> <!ATTLIST zamestnanec id CDATA #REQUIRED> <!ELEMENT jmeno (#PCDATA)> <!ELEMENT prijmeni (#PCDATA)> <!ELEMENT plat (#PCDATA)> <!ELEMENT narozen (#PCDATA)>

Jak vidíme, používá se pro zápis DTD zcela speciální syntaxe s klíčovými slovy jako je <!ELEMENT…> a <!ATTLIST…>, které slouží k deklaraci elementů a atributů. Mezi deklarace lze vkládat komentáře stejně jako v XML.

d
t
d
<!-- Informace o zaměstnanci --> <!ELEMENT zamestnanec (jmeno, prijmeni, plat, narozen)> <!ATTLIST zamestnanec id CDATA #REQUIRED> <!-- Osobní číslo zaměstnance --> <!ELEMENT jmeno (#PCDATA)> <!ELEMENT prijmeni (#PCDATA)> <!ELEMENT plat (#PCDATA)> <!ELEMENT narozen (#PCDATA)>

U DTD se předpokládá, že je uloženo v kódování UTF-8 nebo UTF-16. Máte-li v dokumentu například znaky s diakritikou a z nějakého důvodu musíte použít jiné kódování, je možné jej určit pomocí deklarace kódování (má podobný tvar jako deklarace XML).

d
t
d
<?xml version="1.0" encoding="windows-1250"?> <!-- Informace o zaměstnanci --> <!ELEMENT zamestnanec (jmeno, prijmeni, plat, narozen)> …

Kromě deklarací elementů a atributů nabízí DTD prostředky pro deklaraci entit a notací. Tyto konstrukce však přímo neovlivňují definovanou strukturu dokumentu, a proto se jimi v našem tutoriálu nebudeme zabývat.

2.1 Deklarace elementů

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

d
t
d
<!ELEMENT název elementu obsah elementu>

Jako název elementu můžeme zvolit libovolné jméno, které má tvar dovolený specifikací XML. 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é známe z XHTML. Jejich deklarace by vypadala následovně:

d
t
d
<!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/>.

Protipólem k EMPTY je ANY. Toto klíčové slovo nám zajistí, že element může obsahovat libovolné další elementy (ty však musí být také deklarované) 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žít ho lze například při návrhu a ladění DTD, kdy nechceme najednou napsat celé DTD.

d
t
d
<!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. model obsahu (content model). Model obsahu se používá pro definici elementů, které obsahují další elementy nebo mají smíšený obsah (obsahují již přímo text a elementy). Jeho zápis je přitom hodně podobný regulárním výrazům.

Model obsahu je vždy uzavřen 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:

d
t
d
<!ELEMENT html (head, body)>

Pokud naopak elementy oddělíme znakem ‚|‘, může být v dokumentu uveden pouze jeden z nich. Příklad: potomkem může být dcera nebo syn. V DTD to vyjádříme takto:

d
t
d
<!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 model obsahu (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 modelu obsahu uvedeme pouze jméno elementu, musí být element přítomen právě jednou. Je-li 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:

d
t
d
<!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 a musí obsahovat alespoň jednu kapitolu:

d
t
d
<!ELEMENT kniha (kapitola+)>

Vraťme se k předchozímu příkladu a předpoklá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í.

d
t
d
<!ELEMENT clanek (nazev, autor*)>

Vše můžeme podle potřeby kombinovat. Při troše fantazie lze obejít i to, že nemůžeme specifikovat přesný počet opakování určitých elementů. Chceme-li například vyjádřit, že seznam obsahuje alespoň dvě položky, můžeme použít model obsahu (polozka, polozka+).

Indikátor počtu výskytů můžeme připojit i za celý model obsahu. 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 vůbec.

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:

d
t
d
<!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:

d
t
d
<!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>

2.2 Deklarace atributů

V XML může mít každý element libovolné množství atributů. Atributy se většinou používají pro připojení různých metainformací k elementům. Deklarace atributů pro element má poměrně jednoduchý tvar:

d
t
d
<!ATTLIST jméno elementu deklarace atributů>

Deklarace jednotlivých atributů se skládá ze tří částí. První částí je jméno atributu. Za jménem následuje typ atributu. Poslední část určuje standardní hodnotu atributu, popřípadě zda je používání atributu u daného elementu povinné. Deklarace se opakuje pro každý atribut, který má být u elementu k dispozici.

Nejobecnějším typem atributu je CDATA, který umožňuje jako jeho hodnotu zadat libovolný textový řetězec. Po deklaraci:

d
t
d
<!ATTLIST kniha autor CDATA …>

můžeme v dokumentu používat atribut autor následovně

<kniha autor="Karel Čapek">

Mnohem restriktivnějším typem než CDATA je NMTOKEN. Atribut tohoto typu může obsahovat jedno slovo, které se skládá z písmen, číslic a několika dalších speciálních znaků (platí zde stejné omezení jako pro jména elementů a atributů).

Můžeme použít i typ NMTOKENS. Atribut pak může obsahovat několik slov vyhovujících typu NMTOKEN oddělených mezerou.

d
t
d
<!ATTLIST dokument format NMTOKEN … okraje NMTOKENS …>

V dokumentu pak můžeme použít element dokument například takto:

<dokument format="A4" okraje="2cm 3cm 2.5cm 3cm"></dokument>

Mezi další typy atributů patří ID, IDREF a IDREFS, které se používají pro vytváření odkazů v rámci dokumentu. Pokud atribut definujeme jako ID, musí mít v rámci dokumentu přiřazenu jedinečnou hodnotu. Přitom všechny atributy typu ID sdílí stejný prostor – omezení na jedinečnost platí i pro atributy s různým názvem u různých elementů. Pokud pravidlo jedinečnosti porušíme, ohlásí parser chybu. U jednoho elementu můžeme přitom deklarovat maximálně jeden atribut typu ID.

Atributy s typem IDREF pak mohou obsahovat pouze hodnotu použitou v nějakém atributu typu ID. Typ IDREFS umožňuje použít v jednom atributu více hodnot najednou – podobně jako u NMTOKENS se jednotlivé hodnoty oddělují mezerami.

Poslední možností, jak vymezit typ atributu, je uvedení výčtu přípustných hodnot atributu. Například:

d
t
d
<!ATTLIST obrazek zarovnani (vlevo | vpravo | doprostred) …>

U elementu obrazek nyní můžeme jako hodnotu atributu zarovnani uvést pouze jedno ze slov vlevo, vpravo a doprostred.

Atribut může mít ještě typ ENTITY, ENTITIES nebo NOTATION. Tyto typy se v praxi dnes již téměř nepoužívají, protože souvisejí s možnostmi, který byly do XML převzaty z SGML, ale neukázaly se moc životaschopné.

V deklaraci všech atributů jsme zatím na posledním místě používali tři tečky. Na tomto místě se uvádí standardní hodnota atributu, nebo to, zda je atribut povinný.

Pokud je atribut povinný, uvede se za deklarací jeho typu slovo #REQUIRED. Parser při kontrole dokumentu ohlásí chyby vždy, když nebude tento atribut u elementu použit.

Opačným případem je situace, kdy můžeme atribut vynechat, a je pak věcí aplikace, která dokument zpracovává, jak se v tomto případě zachová. K určení nepovinného atributu slouží klíčové slovo #IMPLIED.

Další možností je specifikování standardní hodnoty, která se použije v případě, kdy atribut není v dokumentu uveden. Chceme-li, aby byl obrázek zarovnán vlevo, pokud není určeno jinak, použijeme deklaraci:

d
t
d
<!ATTLIST obrazek zarovnani (vlevo | vpravo | doprostred) "vlevo">

Před standardní hodnotou atributu můžeme ještě uvést slovo #FIXED. Tím stanovíme, že v dokumentu nemůže mít atribut jinou hodnotu než standardní. Tento postup se dá použít pro uložení metainformací do DTD, které se automaticky doplní do každého dokumentu. Nicméně tento postup se nedoporučuje, jak je vysvětleno v sekci 7.4 – „Defaultní a fixní hodnoty“.

Pokud má jeden element více atributů, je úplně jedno, zda je deklarujeme v jedné deklaraci <!ATTLIST …> nebo v několika postupně. Následující dva zápisy jsou tedy totožné.

d
t
d
<!ATTLIST para align (left|right|center) #IMPLIED id ID #IMPLIED> <!ATTLIST para align (left|right|center) #IMPLIED> <!ATTLIST para id ID #IMPLIED>

2.3 Připojení DTD k dokumentu

DTD se k dokumentu připojuje pomocí deklarace typu dokumentu, která se uvádí bezprostředně za deklarací XML. DTD přitom můžeme napsat přímo do interní podmnožiny deklarace typu dokumentu a je pak součástí dokumentu.

Příklad 2.1. DTD v interní podmnožině – dtd/doctype-internal.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE zamestnanec [
  <!ELEMENT zamestnanec (jmeno, prijmeni, plat, narozen)>
  <!ATTLIST zamestnanec
            id          CDATA   #REQUIRED>
  <!ELEMENT jmeno       (#PCDATA)>
  <!ELEMENT prijmeni    (#PCDATA)>
  <!ELEMENT plat        (#PCDATA)>
  <!ELEMENT narozen     (#PCDATA)>
]>
<zamestnanec id="101">
  <jmeno>Jan</jmeno>
  <prijmeni>Novák</prijmeni>
  <plat>25000</plat>
  <narozen>1965-12-24</narozen>
</zamestnanec>

Tato metoda je však poměrně nepraktická, protože DTD se musí kopírovat do každého dokumentu. Nejčastěji se proto DTD uloží do samostatného souboru a v deklaraci typu dokumentu se pak načte jako externí podmnožina.

Příklad 2.2. Externí DTD – dtd/doctype-external.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE zamestnanec SYSTEM "zamestnanec.dtd">
<zamestnanec id="101">
  <jmeno>Jan</jmeno>
  <prijmeni>Novák</prijmeni>
  <plat>25000</plat>
  <narozen>1965-12-24</narozen>
</zamestnanec>

Oba přístupy je možné kombinovat. To lze využít v případech, kdy chceme pro jeden dokument upravit chování DTD. Lokální deklarace v interní podmnožině mají přednost před deklaracemi z externího DTD. Následující příklad ukazuje, jak atribut id předefinovat jako nepovinný.

Příklad 2.3. Kombinování externích a interních deklarací – dtd/doctype-both.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE zamestnanec SYSTEM "zamestnanec.dtd" [
  <!ATTLIST zamestnanec
            id  CDATA   #IMPLIED>
]>
<zamestnanec>
  <jmeno>Jan</jmeno>
  <prijmeni>Novák</prijmeni>
  <plat>25000</plat>
  <narozen>1965-12-24</narozen>
</zamestnanec>

© Jiří Kosek 2014

Tento dokument je určen výhradně pro osobní potřebu seznámení se schémovými jazyky. Jakékoliv jiné použití, včetně dalšího šíření, pořizování kopií, použití při školeních a výuce apod. je výslovně zakázáno a bude považováno za porušení autorských práv.

Dokument je zkrácenou verzí školicích materiálů používaných během školení XML schémata. Školení se mj. věnuje i problematice data-bindingu a využití informací ze schématu uvnitř aplikací.


Copyright © 2003-2013 Jiří Kosek