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.
Deklarace nového elementu je velice jednoduchá. Má následující tvar:
d
t
d<!ELEMENTná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>
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<!ATTLISTjmé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>
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>