Damit ein Dokument als gültig erkannt werden kann, muss es eine Deklaration des Dokumententyps enthalten, die zwischen der XML-Deklaration und dem öffnenden Tag des obersten Elements stehen muss. Bei dem Beispiel auf der letzten Seite muss dazu als zweite Zeile zum Beispiel
<!DOCTYPE sect SYSTEM "http://www.hars.de/2000/xml/sect.dtd">
eingefügt werden.
Diese Deklaration sagt, dass das oberste Element des Dokuments vom Typ
sect
ist und die DTD den System Identifier http://www.hars.de/2000/xml/sect.dtd hat.
Ein Programm, das das Dokument auf seine Gültigkeit überprüfen (im
Jargon "validieren") will, muss die DTD von der als System Identifier
angegebenen Stelle laden (es geht auch anders, siehe Public Identifier und Kataloge, aber das ist noch
nicht richtig standardisiert).
Deklaration von Elementen
Eine DTD muss für alle in einem Dokument zulässigen Elemente eine
<ELEMENT>
-Deklaration enthalten.
Diese gibt den Namen und das Inhaltsmodell für das Element an, für das
Element sect
zum Beispiel:
<!ELEMENT sect (title?, para+)>
Das Inhaltsmodell besagt, dass in einem sect
-Element
zuerst null oder ein title
Element und dann ein oder
mehr para
-Elemente vorhanden sein müssen.
Um die Häufigkeit, mit der ein Element auftreten kann, festzulegen,
gibt es drei Wiederholungsanzeiger:
?
wenn ein Element null oder ein Mal auftreten kann,
+
wenn es wenigstens ein Mal auftreten muss und
*
wenn es gar nicht oder beliebig oft auftreten darf.
Bei der Verbindung zwischen Elementen in einem Inhaltsmodell steht das
Komma für ein Sequenz, ein vertikaler Strich |
für eine
Alternative.
Mit Klammern können in einem Inhaltsmodell Gruppen gebildet werden.
Ein kompexeres Inhaltsmodell für sect
könnte so aussehen:
(title?, (intro | motto)?, para, (para | figure)*)
Diesen Modell erlaubt nach einem optionalen Titel entweder eine Einleitung oder ein Motto (möglicherweise keines von beiden, aber nie beide), dann einen Absatz und schließlich eine beliebige, möglicherweise leere Folge von weiteren Absätzen und Abbildungen. Das Beispieldokument würde auch diesem Modell entsprechen.
Wenn ein Element keine anderen Elemente, sondern nur Text enthalten
soll, ist sein Inhaltsmodell "Parsed Character Data".
Bei dem Beispiel trifft das etwa auf die Elemente term
und em
zu:
<!ELEMENT term (#PCDATA)> <!ELEMENT em (#PCDATA)>
Interessanter sind Elemente wie para
, die sowohl Text als
auch andere Elemente enthalten können, so genannten gemischten
Inhalt.
Die Deklaration eines Elements mit gemischtem
Inhalt (mixed content) muss die folgende Form haben:
<!ELEMENT para (#PCDATA | term | em)*>
Das heißt, #PCDATA
muss als erstes kommen, als Verbindung
zwischen den zulässigen Elementen ist nur die Alternative
|
zulässig, es darf in dem Inhaltsmodell keine durch
Klammern gebildete Gruppen geben, und der Wiederholungsanzeiger muss
der Stern sein.
Man kann also die Art der zulässigen Elemente einschränken, aber
weder ihre Reihenfolge noch ihre Häufigkeit.
Attribute
Eine DTD legt auch für alle Elemente die zulässigen Attribute mit deren Typen und Default-Werten fest. In dem Beispiel kommen nur zwei Attribute vor, und zumindest diese müssen in der DTD deklariert werden:
<!ATTLIST title id ID #IMPLIED> <!ATTLIST term class NMTOKEN #IMPLIED>
title
hat ein Attribut id
, dessen Typ als
ID
angegeben ist.
Das heißt, der Wert muss ein gülitger Name sein und darf im ganzen
Dokument nur ein einziges mal als Wert eines Attributs vom Typ
ID
vorkommen.
Das Attribut class
soll ein NMTOKEN
sein,
muss also aus den gleichen Zeichen wie ein Name bestehen, ist aber
sonst nicht eingeschränkt.
ID
und NMTOKEN
gehören zu den tokenarigen
Werttypen, wozu auch IDREF
als Referenz auf Elemente mit
einen Attribut von Typ ID
gehört, oder
IDREFS
und NMTOKENS
für Listen von
IDREF
oder NMTOKEN
Token.
Beliebige Zeichenketten (mit der Einschränkung, dass das Zeichen <
in Attributen immer als < eingegeben werden muss) kann ein
Attribut mit dem Typ CDATA
aufnehmen, und für Attribute,
die nur wenige bestimmte Werte annehmen können, steht ein
Aufzählungstyp zur Verfügung.
In den minimalen Beispiel haben beide Attribute den Default-Wert
#IMPLIED
, was so viel heißt dass sie keinen Default haben
und auch nicht unbedingt einen Wert haben müssen.
Der Default könnte auch #REQUIRED
sein, dann muss das
Attribut immer angegeben werden.
Wenn ein anderer Wert angegenben wird, ist dies der Wert des
Attributs, sofern er im Dokument nicht geändert wird.
Schließlich kann ein solcher Default durch die Angabe
#FIXED
als unveränderlich gekennzeichnet werden.
Eine Deklaration für die Attribute von term
, die
mehrere dieser Möglichkeiten nutzt, könnte so aussehen:
<!ATTLIST term id ID #IMPLIED class NMTOKEN #IMPLIED descr CDATA #IMPLIED area (sci | soc | tech) "tech" form NMTOKEN #FIXED "name" >
Die Definition von Elementen und Attributen sind die beiden wichtigsten Konzepte, wer jetzt (oder nach einem der nächsten Abschnitte) hinreichend verwirrt ist, kann jederzeit zu den Ergänzungen und Erweiterungen von XML weitergehen, ohne zu viel zu verpassen.
Parameterentitäten
In realen DTDs würde man para
vermutlich anders
als oben definieren.
Das Inhaltsmodell von para
ist praktisch das gleiche wie
das von Bildunterschriften, Tabellenzellen, Kapiteleinleitungen oder
auch Überschriften:
fortlaufender Text mit einigen Hervorhebungen.
Um diese Gemeinsamkeiten auszudrücken, bietet sich das DTD-Entwurfsmuster "fortlaufender Text" an.
Es bedient sich des Mittels einer Parameterentität, einer Art von
Makros für DTDs:
<!ENTITY % running.text "#PCDATA | term | em"> <!ELEMENT title (%running.text;)*> <!ELEMENT para (%running.text; | fn)*> <!ELEMENT fn (%running.text;)*>
Durch diese Konstruktion wird sicher gestellt, dass Änderungen am
Inhaltsmodell für den eigentlichen Text eines Dokuments (zum Beispiel
die Addition eines Elements name
, um automatisch
Personenverzeichnisse erstellen zu können) an allen Stellen wirksam
werden, an denen fortlaufender Text zulässig ist.
Ein ähnliches Verfahren wendet man auch an, wenn mehrere Elemente die
gleichen Attribute haben können.
Wenn zum Beispiel alle ein id
- und ein
class
-Attribut haben können, würde man die
Parameterentität
definieren, und dann diese in den<!ENTITY % coreattrs " id ID #IMPLIED class NMTOKEN #IMPLIED ">
ATTLIST
-Deklarationen
verwenden.
Allgemeine Entitäten
Parameterentitäten sind Abkürzungen, die man in der DTD selbst verwenden kann. Man kann in einer DTD aber auch Entitäten definieren, die im XML-Dokument benutzt werden können, so genannte allgemeine Entitäten. Die einfachste Form einer allgemeinen Entität, eine interne Entität, wird ohne das für Parameterentitäten typische Prozentzeichen definiert:
<!ENTITY hhlug "Hamburger Linux User Gruppe">
Wenn jetzt in einem XML-Dokument die Entität als
&hhlug;
referenziert wird, erscheint in dem Ergebnis
der Verarbeitung des Dokuments der Ersetzungstext "Hamburger Linux
User Gruppe" (natürlich ohne die Anführungszeichen), sofern
die Anwendung Entitäten auflöst, was sie nicht immer tun muss.
Eine andere Form von allgemeinen Entitäten sind die externen Entitäten:
<ENTITY hhlug-hp SYSTEM "http://www.hhlug.de/index.xhtml">
Wenn diese in einem Dokument mit &hhlug-hp;
referenziert wird, wird der Inhalt der XHTML-Version der Hompage der
&hhlug;
an dieser Stelle eingefügt (wenn es sie denn
gibt, sonst gibt es eine Fehlermeldung).
Die interne Teilmenge der DTD
Die in der DOCTYPE
-Deklaration mit einem
SYSTEM
Identifier (oder auch einem PUBLIC
Identifier referenzierte DTD,
bildet die so genannte externe Teilmenge der DTD.
Ihre Benutzung hat den Vorteil, dass man die gleiche DTD für viele
verschiedene Dokumente benutzen kann.
Es gibt aber auch die Möglichkeit, statt oder zusätzlich zu dieser
externen Teilmenge auch eine interne Teilmenge direkt in der
DOCTYPE
-Deklaration zu definieren.
Im Prinzip kann man (mit einigen Einschränkungen bei der Benutzung von
Parameterentitäten) die ganze DTD in der internen Teilmenge
deklarieren, typischerweise benutzt man diese Möglichkeit aber, um
einzelne Entitäten zu definieren, die nur in einem Dokument benutzt
werden:
<!DOCTYPE sect SYSTEM "http://www.hars.de/2000/xml/sect.dtd" [ <ENTITY hhlug "Hamburger Linux User Gruppe"> ]>
Public Identifier und Kataloge
Es gibt ein Problem bei der Benutzung weit verbreiteter und standardisierter
DTDs, deren System Identifier eine http
-URL ist:
man muss prinzipiell in der Lage sein, auf die URL zuzugreifen, wenn
man ein Dokument validieren will.
Das bedeutet, dass man ohne eine stehende Verbindung zum Internet
eigentliche kein gültiges XML verarbeiten kann!
Eine Lösung für dieses Problem, die auch schon die Definition von XML vorsieht, ist die Benutzung von Public Identifiers, die ein Programm auf eine lokal bekannte DTD abbilden kann. Dazu muss es auf einen lokal vorhandenen Katalog zurückgreifen, der Public Identifier lokalen System Identifiern zuordnet, so dass nur bei einen nicht bekannten Public Identifier tatsächlich über das Netz auf die DTD zugegriffen werden muss. Um einen solchen Mechanismus zu nutzen, müsste die DOCTYPE-Deklaration für das Beispieldokument zum Beispiel so aussehen (einmal davon abgesehen, dass das zuständige Normierungsgremium mir den benutzten Namensraum mit meinem Namen nicht offiziell zugeteilt hat):
<!DOCTYPE sect PUBLIC "-//Florian Hars//Beispiel DTD sect V 1.0//EN" "http://www.hars.de/2000/xml/sect.dtd">
Dies ist in der SGML-Welt eine fest etablierte und standardisierte Praxis. Die XML-Definition sieht zwar Public Identifier vor, sagt aber nicht, wie mit ihnen umzugehen ist. Falls man seinen XML-Dateien mit SGML-Werkzeugen bearbeitet, kann man die Katalog-Funktionen dieser Werkzeuge benutzen, ansonsten muss man auf die Ergebnisse der entsprechenden Diskussionen in den XML-Arbeitsgruppen des W3C warten.
Weiter im Text: Ergänzungen und Erweiterungen von XML.