Eine DTD ist schnell geschrieben, doch wie sieht eine große DTD aus, welche auf das komplexe DTD Vokabular zurückgreift?
In einer DTD sind die Definitionen der verwendeten Elemente in
einem XML Dokument. Das bedeutet, das die DTD dem Element die spezifischen
Eigenschaften gibt. Die DTD bestimmt, ob ein Element Attribute besitzt,
wenn ja, dann welche. Sie definiert die Eigenschaften der Attribute,
und gibt an welche Werte das Element oder die Attribute annehmen
kann.
Es gibt interne und externe DTDs. Interne DTDs sind in dem Dokument
selbst definiert, die Definitionen werden nur auf das Dokument angewendet.
Externe DTDs befinden sich in ausgelagerten Dateien. Diese Dateien
haben die Dateiendung *.dtd. Eine externe DTD muss in das jeweilige
XML-Dokument eingebunden werden.
Das erste Beispiel ist ein einfaches XML Dokument, mit einer genauso
einfachen DTD.
versuch1.xml:
<?xml version="1.0" encoding="ISO-8859-1"
?>
<!DOCTYPE name SYSTEM "name.dtd">
<name>Thiemo</name>
name.dtd:
<!ELEMENT name (#PCDATA)>
Das Ergebnis ist ein gültiges und wohlgeformtes XML-Dokument:
Ein gültiges, wohlgeformtes XML Dokument
Die DTD wird über den Befehl <!DOCTYPE name SYSTEM "name.dtd">
in das Dokument eingebunden. Dieser Befehl hat das Attribut SYSTEM,
nach welchem die Adresse zur DTD angegeben wird.
Die DTD ist nicht sonderlich interessant. Es wird das Element name
definiert.
Das Schema einer Elementdefinition ist immer gleich:
<!ELEMENT Name (Inhalt)>
Als Inhalt wurde hier #PCDATA festgelegt. Dies besagt, das jeder
Inhalt möglich ist. Jedoch ist die Verwendung eines anderen
Elementes innerhalb dieses Elementes strengstens untersagt.
<name><fett>Thiemo</fett></name>
Dieser Code ist mit der DTD ungültig, da nicht definiert wurde,
dass das Element <fett> innerhalb des Elementes <name>
verwendet wird.
Eine Komplexere DTD ist die zur Adressenverwaltung. Man möchte
ein XML Dokument als Adressendatenenbank verwenden. Hierbei soll
die XML Struktur wie folgt aussehen:
adressen.xml:
<?xml version="1.0" encoding="ISO-8859-1"
?>
<!DOCTYPE adressen SYSTEM "adressen.dtd">
<adressen_db>
<adresse>
<anrede>Herr</anrede>
<name>Thiemo Fetzer</name>
<strasse>Bilharzstrasse 1</strasse>
<plz>89081</plz>
<ort>Ulm</ort>
</adresse>
<adresse>
<name>Uni Ulm</name>
<postfach>7561</postfach>
<plz>89077</plz>
<ort>Ulm</ort>
</adresse>
</adressen_db>
Schon bevor man sich mit der DTD beschäftig muss man einige
Grundvorraussetzungen festlegen. Die Datei beeinhaltet zwei Datensätze,
die jeweils eine verschiedene Adresse beinhaltet. Man muss in der
DTD also definieren, das das Element adresse mehr als einmal
im Dokument auftreten kann.
Zudem befinden sich in der Datenbank Anschriften von Gebäuden,
und von Personen. Bei der Anschrift an eine Person ist eine Anrede
vorhanden, bei einer Gebäudeanschrift würde diese keinen
Sinn machen. Deshalb muss das Unterelement andrede als optional
definiert werden. Auch dies ist über die DTD möglich.
Eine weitere Besonderheit die auffällt ist, das im ersten Datensatz
die Strasse und Hausnummer angegeben ist, im zweiten Datensatz nur
das Postfach. Hierbei muss es möglich sein, entweder das Element
strasse festzulegen, oder ein postfach zu definieren.
Dies wird ebenfalls mit der DTD erreicht.
adressen.dtd:
<!ELEMENT adressen_db (adresse)*>
<!ELEMENT adresse (anrede?,
name, (postfach | strasse), plz, ort)>
<!ELEMENT anrede (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT postfach (#PCDATA)>
<!ELEMENT strasse (#PCDATA)>
<!ELEMENT plz (#PCDATA)>
<!ELEMENT ort (#PCDATA)>
Die erste Eigenschaft der DTD, die wir ermittelt haben, ist,
dass das Element adresse mehrmals vorkommen kann. Dies wird
durch das * bei der Definition des Wurzelelementes adressen_db
erreicht.
<!ELEMENT adressen_db (adresse)*>
Diesem Wurzelelement wird das Unterelement adresse zugewiesen,
welches durch das * die Eigenschaft hat mehrmals vorzukommen, es
kann aber auch leer sein.
Danach wird das Unterelement adresse genauer definiert.
<!ELEMENT adresse (anrede?, name,
(postfach | strasse), plz, ort)>
Wir hatten festgestellt, das in dem Dokument die Anrede als optional
gelten soll. Dies wird durch das anrede?
erreicht. Es besagt, das das Element vorkommen kann, aber nicht
zwingend definiert werden muss.
Den nächsten Zustand der ermittelt wurde war, das entweder
ein Postfach, oder eine Strasse angegeben wird. Dies wird durch
(postfach | strasse) erreicht.
Der | dient hier als Operator, welcher das "oder" ausdrückt.
Eines dieser Elemente muss jedoch definiert sein.
Die Elemente name, plz und ort sind "normale"
Elemente, die gesetzt werden müssen. Die genauere Definition
der einzelnen Unterelemente erfolgt darunter.
Wie Sie an diesem Beispiel gesehen haben gibt es viele Möglichkeiten
eine DTD zu verfeinern, und die Inhalte abzustimmen.
Ein Problem ist jedoch immer noch vorhanden. Was ist, wenn man über
ein Skript z.B. einen Datensatz ermitteln will?
Das Problem liegt einzig und allein darin, das die Datensätze
nicht genauer Definiert sind. Sie sind nicht einmalig. Diese Eigenschaft
wurde durch
<!ELEMENT adressen_db (adresse)*>
erreicht. Es besagt, dass das Element adresse mehrmals vorkommen
darf. wir müssen jetzt dem Element adresse ein Attribut
geben, welches es eindeutig bezeichnet, gewissermaßen
eine ID.
Die Struktur des XML-Dokumentes sähe dann wie folgt aus:
<?xml version="1.0" encoding="ISO-8859-1"
?>
<!DOCTYPE adressen SYSTEM "adressen.dtd">
<adressen_db>
<adresse nr="1">
<anrede>Herr</anrede>
<name>Thiemo Fetzer</name>
<strasse>Bilharzstrasse 1</strasse>
<plz>89081</plz>
<ort>Ulm</ort>
</adresse>
<adresse nr="2">
<name>Uni Ulm</name>
<postfach>7561</postfach>
<plz>89077</plz>
<ort>Ulm</ort>
</adresse>
</adressen_db>
Wie Sie sehen wurde ein eindeutiger Bezeichner nr=""
für jeden Datensatz eingeführt. Jeder Datensatz ist also
einmalig. Daraus kann man jetzt schon schließen, das die DTD
auf eine Wiederholungsanweisung für das Element adresse durch
den * verzichten kann, da jedes Element einmalig bezeichnet ist.
adressen.dtd
<!ELEMENT adressen_db (adresse)>
<!ELEMENT adresse (anrede?,
name, (postfach | strasse), plz, ort)>
<!ATTLIST adresse
nr CDATA #REQUIRED
>
<!ELEMENT anrede (#PCDATA)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT postfach (#PCDATA)>
<!ELEMENT strasse (#PCDATA)>
<!ELEMENT plz (#PCDATA)>
<!ELEMENT ort (#PCDATA)>
Die DTD ist geringfügig verändert worden. Eine Zeile
wurde hinzugefügt.
<!ATTLISTadresse
nr CDATA #REQUIRED
>
Diese Zeile definiert die Attribute für das Attribut adresse.
Das Schema dieser Definition ist immer gleich:
<!ATTLIST Elementname Attributname
Inhalt [#REQUIRED|#IMPLIED|#FIXED "Fester
Wert "|"Defaultwert"]>
Es können beliebig viele Attribute gesetzt werden. Jedes Attribut
muss in einer eigenen Zeile definiert werden. In unserem Beispiel
wurde als Inhalt CDATA definiert. Dieser Bezeichner gibt an, das
der Inhalt des Attributes aus Zeichendaten besteht.
Die Bezeichner #REQUIRED, #IMPLIED, #FIXED
geben die Eigenschaften des Inhaltes genauer an.
Der Bezeichner #REQUIRED gibt an, das dieses Attribut gesetzt
werden muss. Der Bezeichner #IMPLIED gibt an, das die Setzung
des Attributes optional ist (es muss nicht gesetzt werden).
Bei der Angabe #FIXED "Fester Wert" wird dem Attribut
ein fester Wert gegeben. Das Attribut muss nicht mehr gesetzt werden,
es wird automatisch gesetzt, mit dem Wert, der hinter #FIXED angegeben
ist.
Ein sogenannter Defaultwert gilt für alle Attribute.
Ist es nicht gesetzt, dann nimmt es diesen Wert an.
Man kann den Attributen auch direkt einen festen Wert geben, oder
eine reihe von Werten bestimmen, welche verwendet werden dürfen.
websites.xml
<?xml version="1.0" encoding="ISO-8859-1"
?>
<!DOCTYPE websites SYSTEM "websites.dtd">
<websites>
<website nr="1" punkte="5">
<name>devmag.net</name>
<url>https://www.devmag.net</url>
</website>
<website nr="2" punkte="4">
<name>XML-Magazin</name>
<url>http://www.xml-magazin.de</url>
</website>
</websites>
Die Datensätze haben ein weiteres Attribut erhalten. Das punkte
Attribut gibt an, wieviele Punkte die Seite hat. Die maximale Punktzahl
ist 5, die minimale Punktzahl ist 1. Damit das Dokument gültig
ist kann nur aus diesen fünf Werten ausgewählt werden.
<!ELEMENT websites (website)>
<!ELEMENT website (name, url)>
<!ATTLIST adresse
nr CDATA #REQUIRED
punkte (1|2|3|4|5) #REQUIRED
>
<!ELEMENT name (#PCDATA)>
<!ELEMENT url (#PCDATA)>
Der Schlüssel liegt in der Definition des Attributes punkte.
punkte (1|2|3|4|5) #REQUIRED
In der Klammer werden die möglichen Werte angegeben. Diese
sind mit einem | voneinander getrennt. Einer dieser Werte muss jedoch
ausgewählt sein. Das verlangt die Eigenschaft #REQUIRED.
Diese Einführung in DTDs ist sehr komplex. Die Arbeit mit DTDs
ist am Anfang sehr schwer zu verstehen, jedoch zeigen sich schnell
die Möglichkeiten, und die Einfachheit der DTDs lässt
sich auch schnell entdecken.