UNIVERSITÄT GIESSEN
Institut für Informatik
Dr. Stefan Brass
Aus der Spezifikation, Abschnitt 2.8: ``The XML document type declaration contains or points to markup declarations that provide a grammar for a class of documents. This grammar is known as a document type definition, or DTD.'' [http://www.w3.org/TR/REC-xml#sec-prolog-dtd]
Man kann eine gegebene DTD direkt in eine kontextfreie Grammatik (CFG) übersetzen. Dabei führt man für jeden Element-Typ ein entsprechendes Nichtterminal-Symbol ein. Die Regel, daß Attribute in beliebiger Reihenfolge angegeben werden können, aber keines mehrfach, ist in der CFG allerdings nur umständlich auszudrücken (Außerdem können ID/IDREF-Attribute (s.u.) nicht kontextfrei dargestellt werden.). Umgekehrt kann man eine kontextfreie Grammatik auch in eine DTD übersetzen (wieder mit der Entsprechung Nichtterminalsymbol - Elementtyp), aber man behält dann mit den Start- und End-Tags Markierungen zurück, die den ganzen Ableitungsbaum im Ergebniswort codieren. Man könnte also vielleicht sagen, daß man eine beliebige kontextfreie ``abstrakte Syntax'' als DTD darstellen kann, aber die konkrete Syntax dann von XML vorgeschrieben wird.
Dies wird unten noch näher erläutert (siehe unter ``Dokument-Typ Deklaration'')
<!-- Document Type Definition für Literaturlisten -->
<!ELEMENT BOOKLIST (BOOK)*>
<!ELEMENT BOOK (AUTHOR+, TITLE, PUBL?, NOTE?)>
<!ATTLIST BOOK ISBN CDATA #REQUIRED
PAGES CDATA #IMPLIED>
<!ELEMENT AUTHOR EMPTY>
<!ATTLIST AUTHOR FIRST CDATA #IMPLIED
MI CDATA #IMPLIED
LAST CDATA #REQUIRED>
<!ELEMENT TITLE (#PCDATA)>
<!ELEMENT PUBL (#PCDATA)>
<!ATTLIST PUBL YEAR CDATA #IMPLIED
MO CDATA #IMPLIED>
<!ELEMENT NOTE (#PCDATA)>
<?xml version="1.0"?>
<!-- Kommentar: Einige Bücher zur XML-Definition -->
<!DOCTYPE BOOKLIST SYSTEM "booklist.dtd">
<?xml-stylesheet type="text/xsl" href="books.xsl"?>
<BOOKLIST>
<BOOK ISBN="0-13-014714-1" PAGES="1074">
<AUTHOR FIRST="Paul" LAST="Prescod"/>
<AUTHOR FIRST="Charles" MI="F." LAST="Goldfarb"/>
<TITLE>The XML Handbook - 2nd Edition</TITLE>
<PUBL YEAR="1999" MO="11">Prentice Hall</PUBL>
<NOTE>Contains CD.</TITLE>
</BOOK>
<BOOK ISBN="1-56592-709-5" PAGES="107">
<AUTHOR FIRST="Robert" LAST="Eckstein"/>
<TITLE>XML Pocket Reference</TITLE>
<PUBL YEAR="1999" MO="10">O'Reilly</PUBL>
</BOOK>
<BOOK ISBN="0-13-082676-6" PAGES="368">
<AUTHOR FIRST="Bob" LAST="DuCharme"/>
<TITLE>XML: The Annotated Specification</TITLE>
<PUBL YEAR="1998" MO="12" >Prentice Hall</PUBL>
</BOOK>
</BOOKLIST>
Alle Deklarationen in XML beginnen mit ``<!''.
Natürlich kann ein Element bei der dritten Möglichkeit (``element content'') indirekt Text enthalten, nämlich innerhalb dieser Elemente. Aber die Kind-Knoten in der Baum-Darstellung sollen dann sämtlich Element-Knoten sein, keine Text-Knoten.
<!ELEMENT AUTHOR EMPTY>
Es wäre übrigens ein Syntaxfehler, Klammern um das Wort ``EMPTY'' zu setzen. In diesem Fall müßten ``AUTHOR''-Elemente jeweils genau ein Element vom Typ ``EMPTY'' enthalten. Man beachte auch, daß wenn man Start- und End-Tag für ein als leer deklariertes Element verwendet, dazwischen nicht einmal ein Leerzeichen stehen darf.
<!ELEMENT TITLE (#PCDATA)>
Dies zählt formal als ``mixed content'', obwohl es gar nicht gemischt ist (reiner Text). ``#PCDATA'' steht für ``parsed character data''. Im Unterschied zu CDATA (``character data'')-Abschnitten muß der Inhalt hier syntaktisch analysiert werden, um zu prüfen, daß auch wirklich keine Elemente darin vorkommen (und um Entity-Referenzen zu ersetzen). Hier sind übrigens die Klammern nötig, sonst wäre es ein Syntaxfehler. (Ich kenne keine einleuchtende Erklärung, warum die Klammern nötig sind.) Die Markierung ``#'' ist nötig, um es als Schlüsselwort zu kennzeichnen. Läßt man es weg, so würde der Parser statt dessen ein Element vom Typ ``PCDATA'' erwarten.
Solcher Inhalt wird in folgender Form definiert:<P>Ein Komitee unter Leitung von <NAME>Charles Goldfarb</NAME>
entwickelte <DEF>SGML</DEF>, die Standard Generalized Markup Language,
von 1978 bis 1986 zu einem internationalen Standard. </P>
<!ELEMENT P (#PCDATA | DEF | NAME)*>
Der senkrechte Strich ``|'' bedeutet ``oder'', der Stern ``*'' bedeutet null oder mehrmalige Wiederholung. Der Inhalt von Elementen des Typs P ist also eine beliebige Folge von Text-Daten, Elementen vom Typ DEF und Elementen vom Typ NAME. Die Elemente müssen nicht unbedingt vorkommen und können aber auch mehrfach vorkommen.
XML bietet keine Möglichkeit, zu spezifizieren, daß die Elemente vorkommen müssen, nur in bestimmter Reihenfolge vorkommen dürfen, oder nicht mehrfach vorkommen dürfen. (Alles dies wird aber möglich bei ``element content'', wenn man den Text also noch in Elemente einschließen würde.) Die Inhalts-Spezifikation muß übrigens genau so aussehen: Obwohl ``|'' normalerweise kommutativ sein sollte, muß ``#PCDATA'' ganz am Anfang stehen. Für die anderen Element-Typen kommt es dagegen auf die Reihenfolge nicht an, es darf aber kein Element-Typ mehrfach angegeben werden.
Leerzeichen etc. sind links und rechts vom senkrechten Strich erlaubt, aber nicht notwendig. Sie sind auch links und rechts von der öffnenden Klammer erlaubt, aber nur links von der schließenden Klammer. Zwischen ``)'' und ``*'' darf kein Leerzeichen stehen. Das ist schon ein bißchen merkwürdig. Am einfachsten merktman es sich so, daß Leerzeichen überall erlaubt sind, wo man es erwarten würde, außer vor dem Wiederholungssymbol.
Es scheint, daß dies äquivalent ist zu<!ELEMENT EXAMPLE ANY>
Dabei ist ... eine Aufzählung aller deklarierten Element-Typen.<!ELEMENT EXAMPLE (#PCDATA|...)*>
Auch bei ANY müssen alle im Dokument verwendeten Element-Typen mit ``<!ELEMENT'' deklariert sein, sonst ist das Dokument nicht gültig.
<!ELEMENT BOOK (AUTHOR, TITLE, PUBLISHER)>
Links und rechts der Kommata sind Leerzeichen erlaubt, aber nicht nötig.
Dies bedeutet, daß es mehr als einen Autoren geben kann (aber mindestens einen geben muß), und daß der Publisher entfallen kann. Es muß genau einen Titel geben, und kann nicht mehr als einen Verlag geben. Die Reihenfolge ist: Erst die Autoren, dann der Titel, und zum Schluß der Verlag (falls vorhanden). Möchte man erlauben, daß es auch gar keine Autoren kann, so muß man das ``+'' durch ein ``*'' ersetzen.<!ELEMENT BOOK (AUTHOR+, TITLE, PUBLISHER?)>
Zwischen den Namen der Element-Typen und den Zeichen +, *, ? sind keine Leerzeichen erlaubt. Wieder ist die allgemeine Regel die, daß Leerzeichen überall erlaubt sind, wo man es erwarten würde, außer vor dem Wiederholungs-/Optionalitäts-Symbol. Leerzeichen sind natürlich auch nicht nach <, <! oder <? erlaubt (aber schon vor >).
Dies erlaubt mehrere Paare von Vorname und Nachname, z.B.:<!ELEMENT AUTHORS (FIRST, LAST)+>
<AUTHORS><FIRST>Udo</FIRST><LAST>Lipeck</LAST>
<FIRST>Stefan</FIRST><LAST>Brass</LAST></AUTHORS>
Man beachte, daß in dem letzten Beispiel der Leerplatz (neue Zeile, Tabulator) zwischen den Elementen zulässig ist, obwohl die Inhaltsspezifikation reinen ``Element Content'' vorschreibt. Dies vereinfacht die Eingabe und erlaubt, XML Code schöner zu formatieren. Wenn der XML Parser aufgrund der DTD weiß, daß dort eigentlich kein Leerplatz erlaubt ist, muß er den Text zwar immernoch an die Anwendung weitergeben, aber ihr gleichzeitig mitteilen, daß dieser Leerplatz unwesentlich ist.
Es müssen dann zunächst Paare von Vornamen und Nachnamen angegeben werden (mindestens einmal), dann ein Titel, und zum Schluß möglicherweise ein Verlag.<!ELEMENT BOOK ((FIRST, LAST)+, TITLE, PUBLISHER?)>
daß jedes ADDRESS-Element genau eines der drei Elemente ``POSTAL'', ``EMAIL'' oder ``PHONE'' enthalten muß.<!ELEMENT ADDRESS (POSTAL | EMAIL | PHONE)>
``Genau eines'' bedeutet mindestens eins und maximal eins. Die Auswahl wird selten ganz pur verwendet, sondern normalerweise innerhalb einer Wiederholung oder einer Sequenz.
Dies erlaubt anstelle der Angabe eines Verlages auch die Angabe einer Universität (etwa für technische Berichte). Aufgrund des ``?'' kann die Angabe aber auch ganz entfallen.<!ELEMENT BOOK ((FIRST, LAST)+, TITLE, (PUBLISHER|UNI)?)>
<!ELEMENT A (B)>
Diese Situation kommt allerdings nicht besonders häufig vor. Namen außerhalb von Klammern sind nicht möglich, da es dann zur Kollision mit ``EMPTY'' und ``ANY'' kommen kann.
Dagegen ist folgendes natürlich nicht äquivalent:<!ELEMENT A (B*)>
<!ELEMENT A (B)*>
<!ELEMENT A (B*, C*)>
<!ELEMENT A (B, C)*>
Wenn der Parser das A liest, muß er sich zwischen den beiden Alternativen entscheiden, ohne zu wissen, ob danach ein B oder ein C folgt.<!ELEMENT EXAMPLE ((A, B) | (A, C))>
Dies beschreibt das gleiche Format für den Inhalt, ist aber ein ``deterministic content model''.<!ELEMENT EXAMPLE (A, (B | C))>
Aufgabe: Geben Sie eine äquivalente deterministische Inhalts-Spezifikation an.<!ELEMENT EXAMPLE2 ((A, B)*, (A, C))>
<!ATTLIST AUTHOR FIRST CDATA #IMPLIED
MI CDATA #IMPLIED
LAST CDATA #REQUIRED>
Tatsächlich läßt die XML Grammatik auch null Attribut-Deklarationen in einer ``<!ATTLIST''-Deklaration zu, aber dann braucht man die ganze Deklaration nicht.
In SGML war das Zeichen ``#'' notwendig, weil ``REQUIRED'' auch ein Default-Wert (s.u.) sein konnte. In XML müssen die Default-Werte dagegen immer in Anführungszeichen eingeschlossen werden, so daß auch ohne ``#'' keine Verwechselung möglich wäre. Aber aus Kompatibilitätsgründen wollte man die Schlüsselwörter nicht ändern.
Falls bei einem Element kein Wert angegeben ist, wird der Anwendung mitgeteilt, daß es keinen Wert für das Attribut gibt (es wird also nicht ein Default-Wert angenommen). Es wird dann davon ausgegangen, daß die Anwendung selbst einen Wert für das Attribut berechnen kann (etwa eine fortlaufende Kapitel-Nummer), daher der Name ``Implied''. Dies ist aber nicht erforderlich, und das Schlüsselwort ``#OPTIONAL'' wäre wohl sinnvoller gewesen.
Dann ist ``<BOOK>'' äquivalent zu<!ATTLIST BOOK OWNER CDATA "Library">
Man kann aber auch explizit einen anderen Wert angeben:<BOOK OWNER="Library">
<BOOK OWNER="SB">
Dies bedeutet, daß das Attribut nur den einen Wert haben kann, und dieser auch gleichzeitig Default-Wert ist, also nicht explizit angegeben werden braucht.<!ATTLIST P xml:space CDATA #FIXED "preserve">
Dies ist nur interessant für ``globale Attribute'', die also mehr als einem Element-Typ zukommen. Für die verschiedenen Element-Typen können dann unterschiedliche Werte deklariert werden. Zum Beispiel die XLink-Spezifikation macht von solchen Attributen Gebrauch, um Element-Typen als Hyperlinks zu kennzeichnen.
Allerdings wird von beiden Möglichkeiten abgeraten, und XML Parser können eine Option anbieten, in solchen Fällen eine Warnung auszugeben.
<!ATTLIST MEAL VEGETARIAN (yes|no) "no"
COOKED (rare|medium|well-done) #IMPLIED>
Man beachte, daß in der Aufzählung keine Anführungszeichen erlaubt sind, aber man einen Default-Wert hinterher in Anführungszeichen angeben muß.
<!ATTLIST BOOK ID ID #IMPLIED>
Es macht keinen Sinn, für Attribute dieses Typs einen Default-Wert anzugeben, und die Spezifikation schreibt auch vor, daß sie nur zusammen mit #REQUIRED und #IMPLIED verwendet werden können.
Kein Element-Typ kann mehr als ein Attribut dieses Typs haben. Dies ist ein Problem für XHTML, weil in HTML jedes Element ein Attribut ``ID'' hat (zur Identifikation in Javascript-Programmen), aber ``A''-Elemente noch zusätzlich das Attribute ``NAME'' haben, das ebenfalls eine eindeutig sein muß.
Man beachte, daß Nummern nicht zulässig als Wert von ID-Attributen sind. Das ist schade, weil in Datenbanken häufig Kundennummern, Angestellten-Nummern, etc. verwendet werden. Man kann sich natürlich behelfen, indem man einen Buchstaben voranstellt, z.B. ``K1001'' für Kunden 1001. Das kann auch nützlich sein, weil IDs in XML global eindeutig sein müssen. Es kann z.B. nicht ein Element vom Typ ``KUNDE'' und ein Element vom Typ ``WARE'' mit der gleichen ID geben. In Datenbanken beziehen sich Schlüssel dagegen immer nur auf eine Relation.
Natürlich kann es mehrere Verweise auf das gleiche Element geben. Der Verweis kann im Dokument auch textuell vor dem Element stehen, auf das verwiesen wird. Die ``ID'' muß also an dieser Stelle noch nicht definiert sein, vorwärts-Referenzen sind möglich.
Es ist in XML DTDs nicht möglich, vorzuschreiben, auf welchen Typ von Elementen verwiesen wird. Natürlich kann man ein Attribut vom Typ ``IDREF'' so verwenden, daß z.B. immer nur auf Produkte verwiesen wird. Aber dies kann in der DTD nicht vorgeschrieben werden, und ein XML Parser würde keine Fehlermeldung ausgeben, wenn so ein Verweis einmal auf einen Kunden zeigt.
Wie vorher muß jeder einzelne Name im XML Dokument als Wert eines Attributes vom Typ ID auftreten.
Es gibt wieder keine Beschränkung bezüglich des ersten Zeichens. Zahlen wie 3.14 sind also zulässig, aber zum Beispiel auch ``.:X-_''. DTDs erlauben nicht, vorzuschreiben, daß nur numerische Werte für ein Attribut zulässig sind (siehe aber den Vorschlag für XML Schemas).
<BOX COLOR="0 50 100">
Im Datenbank-Entwurf, besonders bei der Normalisierung, versucht man solche Redundanzen zu vermeiden. Redundanzen führen häufig zu Inkonsistenzen und doppelter Arbeit.
<?xml version="1.0"?>
<!DOCTYPE BOOKLIST SYSTEM "books3.dtd">
<BOOKLIST>
<AUTHOR ID="PP" FIRST="Paul" LAST="Prescod"/>
<AUTHOR ID="CG" FIRST="Charles" MI="F." LAST="Goldfarb"/>
<AUTHOR ID="RE" FIRST="Robert" LAST="Eckstein"/>
<AUTHOR ID="BC" FIRST="Bob" LAST="DuCharme"/>
<BOOK AUTHORS="PP CG" ISBN="0-13-014714-1" PAGES="1074">
<TITLE>The XML Handbook - 2nd Edition</TITLE>
<PUBL YEAR="1999" MO="11">Prentice Hall</PUBL>
<NOTE>Contains CD.</TITLE>
</BOOK>
<BOOK AUTHORS="RE" ISBN="1-56592-709-5" PAGES="107">
<TITLE>XML Pocket Reference</TITLE>
<PUBL YEAR="1999" MO="10">O'Reilly</PUBL>
</BOOK>
<BOOK AUTHORS="BC" ISBN="0-13-082676-6" PAGES="368">
<TITLE>XML: The Annotated Specification</TITLE>
<PUBL YEAR="1998" MO="12" >Prentice Hall</PUBL>
</BOOK>
</BOOKLIST>
<!ELEMENT BOOKLIST (AUTHOR | BOOK)*>
<!ELEMENT AUTHOR EMPTY>
<!ATTLIST AUTHOR ID ID #REQUIRED
FIRST CDATA #IMPLIED
MI CDATA #IMPLIED
LAST CDATA #REQUIRED>
<!ELEMENT BOOK (TITLE, PUBL?, NOTE?)>
<!ATTLIST BOOK AUTHORS IDREFS #IMPLIED
ISBN CDATA #REQUIRED
PAGES CDATA #IMPLIED>
<!ELEMENT TITLE (#PCDATA)>
<!ELEMENT PUBL (#PCDATA)>
<!ATTLIST PUBL YEAR CDATA #IMPLIED
MO CDATA #IMPLIED>
<!ELEMENT NOTE (#PCDATA)>
<MEAL VEGETARIAN=" yes ">
Um ``valid'' zu sein, muß es eine Document Type Deklaration enthalten, und Elemente und Attribute müssen im Dokument gemäß den Einschränkungen dieses Dokumenttyps verwendet werden.
Wobei die XML Deklaration eventuell entfallen kann (siehe Kapitel 2). Zwischen der XML-Deklaration und der Dokument-Typ Deklaration sowie zwischen der Dokument-Typ Deklaration und dem Start-Tag des Wurzel-Elementes können nur Kommentare und Processing Instructions stehen.
<?xml version="1.0"?>
<!-- Kommentar: Einige Bücher zur XML-Definition -->
<!DOCTYPE BOOKLIST [
<!ELEMENT BOOKLIST (BOOK)*>
<!ELEMENT BOOK (AUTHOR+, TITLE, PUBL?, NOTE?)>
<!ATTLIST BOOK ISBN CDATA #REQUIRED
PAGES CDATA #IMPLIED>
...
]>
<BOOKLIST>
<BOOK ISBN="0-13-014714-1" PAGES="1074">
<AUTHOR FIRST="Paul" LAST="Prescod"/>
<AUTHOR FIRST="Charles" MI="F." LAST="Goldfarb"/>
<TITLE>The XML Handbook - 2nd Edition</TITLE>
<PUBL YEAR="1999" MO="11">Prentice Hall</PUBL>
<NOTE>Contains CD.</TITLE>
</BOOK>
...
</BOOKLIST>
Die Standards sprechen immer von URI (``Uniform Resource Identifier''). Dies soll allgemeiner als URLs sein, und z.B. auch Dateien verwalten können, die auf mehreren rechnern gespiegelt sind. Es ist aber schon lange ``Work in Progress'', und der einzige wirklich verwendete Spezialfall sind bisher URLs. Wenn URIs aber einmal spezifiziert sind, und auch praktisch verwendet werden, brauchen die XML Standards nicht geändert zu werden, weil sie immer das Wort URI anstatt URL verwenden.
Markup-Deklarationen in anderen Dateien werden dagegen nur von einem ``validating parser'' gelesen, der ohnehin etwas komplizierter sein muß. Die Einschränkungen beziehen sich auf Entities (im ``internal subset'' sind Entity-Referenzen nur zwischen Markup-Deklarationen erlaubt, können also nur komplette Deklarationen enthalten) und bedingte Abschnitte (im ``internal subset'' verboten) und werden daher im nächsten Kapitel erläutert.
Das mag zunächst der Intuition wiedersprechen, weil sie hinter dem Verweis auf die externen Deklarationen stehen. Aber auf diese Weise können Attribut-Deklarationen aus der externen datei überschrieben werden (die erste Deklaration zählt). Dies kann z.B. für Default-Werte nützlich sein. Noch wichtiger ist dieses Verhalten aber für Entity-Deklarationen (siehe nächstes Kapitel). Zum Beispiel kann man bedingte Abschnitte in der DTD-Datei mit einem im ``internal subset'' deklarierten Entity auswählen.
Wenn sich XHTML durchsetzt, werden die meisten XML Prozessoren mit einer lokalen Kopie der XHTML DTDs kommen. In den XHTML Dokumenten kann man dann z.B. den Public Identifier "-//W3C//DTD XHTML 1.0 Strict//EN" verwenden. In einer Konfigurationsdatei des XML Prozessors werden Public Identifier auf lokale Dateien abgebildet. Daher muß nicht jedesmal auf den WWW Server des W3C zugegriffen werden. Würde man dagegen den System Identifier "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" verwenden, so wäre das unvermeidbar.
<?xml version="1.0"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" >
<HTML>
...
</HTML>
Zum Beispiel ist nicht einmal das W3C registriert.
Zum Beispiel gibt es Public Identifier auch für Notationen, siehe Kapitel 4.
Normalerweise schreibt man die Sprachkürzel klein und die Landeskürzel groß, aber bei DTDs scheint die große Schreibweise üblich zu sein.
An jeder Stelle, an der ein Leerzeichen stehen darf, kann auch eine beliebige Folge von Leerplatz-Zeichen stehen (zum Beispiel ein Zeilenumbruch, Public Identifier sind oft sehr lang). Auch wird Leerplatz am Anfang und am Ende entfernt, bevor ein Vergleich mit den in der Konfigurationsdatei eingetragenen Public Identifiern durchgeführt wird.
Public Identifier waren wichtig für SGML in der Vor-WWW Zeit. Die oben genannten Leistungsprobleme könnte man vielleicht durch lokale Pufferung der DTD-Dateien umgehen.
© Stefan Brass (sbrass@sis.pitt.edu), 11. September 2000
Original URL: http://www.informatik.uni-giessen.de/staff/brass/xml00/c3_dtd.xml