Dokumenttyp-Definitionen

(Auszug aus "Python & XML" von Christopher A. Jones & Fred L. Drake, Jr.)

Wie zuvor erwähnt, sind Dokumenttyp-Definitionen, kurz DTDs, die in der XML 1.0-Empfehlung angegebene Form von Dokumenttypen. Obwohl es Alternativen dazu gibt, bleiben DTDs eine der meistverwendeten Arten, einen Dokumenttyp zu spezifizieren. In diesem Abschnitt erläutern wir die Syntax der verschiedenen Deklarationen, die in der Dokumenttyp-Deklaration auftauchen können; sie können alle sowohl in externen wie auch in internen Teilmengen vorkommen.

Entity-Deklarationen

Entities sind Datenquellen, die verwendet werden, um ein größeres Konstrukt zusammenzusetzen. Mit den meisten davon, allgemeine Entities genannt, werden Dokumente konstruiert, aber einige, als Parameter-Entities bekannt, werden zur Konstruktion des Dokumenttyps selbst benutzt. Beide werden mit einer Entity-Deklaration in der Dokumenttyp-Definition definiert. Jede Art von Entity wird in einem separaten Namensraum definiert; es kann ein allgemeines Entity namens myEntity und ein Parameter-Entity gleichen Namens geben, ohne daß die Namen kollidieren.

Entities können mehr als einmal deklariert werden – die erste Definition eines Namens hat Vorrang. Das ermöglicht der internen Teilmenge, die Definition der externen Teilmenge zu überschreiben; bei Verwendung von Parameter-Entities können mit diesem Mechanismus DTDs erweitert werden. Die Erweiterung von Dokumenttypen funktioniert meistens dann am besten, wenn die zu erweiternde DTD sorgfältig unter diesem Aspekt entworfen wurde. Die DocBook-DTD für technische Dokumentation ist dafür ein exzellentes Beispiel.

Allgemeine Entities können eine Vielzahl von Formen annehmen: Es können geparste Entities, bestehend aus XML-Text, sein oder ungeparste, z. B. eine Bilddatei, gespeichert im Format Portable Network Graphics (PNG). Der Text eines geparsten Entity darf in der Entity-Deklaration enthalten oder in einer externen Quelle abgelegt sein. Der Rumpf eines ungeparsten Entity ist immer extern gespeichert. Die meisten mit XML verwendeten Entities sind geparste Entities; ungeparste Konstrukte wie z. B. Bilder werden üblicherweise mit einer absoluten oder relativen URL referenziert statt mit einem benannten Entity.

Geparste allgemeine Entities werden zur Definition von Ersetzungstext für einen (normalerweise) kürzeren Namen verwendet. Erinnern Sie sich, daß Text in XML nicht nur Zeichendaten, sondern auch Auszeichnungen enthält, und somit kann die Ersetzung dem Dokument eine zusätzliche Struktur hinzufügen, jedenfalls solange alle Strukturen innerhalb der Ersetzung vollständig sind. In Produktion löst ein Parser die Entities in ihre Ersetzungstexte auf und evaluiert das Dokument nach der Ersetzung der Entities erneut. Ein einfaches internes Entity ist genauso einfach zu erzeugen wie ein Symbol und dessen Ersetzungstext:

 <!ENTITY sandwich "Krabben-Pastetchen"> 

Jede Referenz auf &sandwich; wird überall in Ihrem Dokument durch den Text »Krabben-Pastetchen« ersetzt. Zum Beispiel:

Ich habe Hunger auf ein &sandwich;.

Dieser Satz wird folgendermaßen dargestellt:

Ich habe Hunger auf ein Krabben-Pastetchen.

Externe Entities werden mit einer Entity-Deklaration definiert, die eine URL zu einer externen Ressource angibt, die den Ersetzungstext enthält:

 <!ENTITY legal SYSTEM "http://www.example.com/legal.xml"> 

Alle Referenzen auf &legal; in einem Dokument ergeben:

 <legal>Copyright 2001, Example GmbH</legal>. 

Externe Entities ersetzen Symbole durch den entsprechenden Text, wie interne auch. Manchmal muß dies geschehen, wenn der Text Zeichen enthält, die sonst als Auszeichnungen betrachtet würden (z. B. besondere Zeichen wie < , > und & in Ihrem XML-Code). Und in anderen Fällen werden Entities benutzt, um vorformulierte Informationen zu enthalten, die normalerweise anderswo verwaltet und dem Dokument verfügbar gemacht werden.

Parameter-Entities unterscheiden sich davon sowohl im Gebrauch wie auch in der Anwendbarkeit. Man kann sie nur zur Erstellung von Dokumenttyp-Definitionen benutzen, nicht jedoch, um ein Dokument selbst zusammenzusetzen. Die Syntax eines XML-Dokuments gestattet es nicht, daß Parameter-Entities aus dem Dokumentinhalt referenziert werden, sondern sie ermöglicht deren Gebrauch nur in internen und externen DTD-Teilmengen. Es gibt keine ungeparsten Parameter-Entities, obwohl ein nicht-validierender Parser sie ignorieren darf. Validierende Parser müssen alle referenzierten Parameter-Entities parsen.

Die Deklaration eines Parameter-Entity sieht ähnlich aus wie die Deklaration eines allgemeinen Entity, mit nur wenigen zusätzlichen Zeichen:

 <!ENTITY % node-decls SYSTEM "node-decls.dtd"> 

Hier kommt etwas vor, was in der allgemeinen Entity-Deklaration nicht vorkommt, nämlich das Prozentzeichen (%) zwischen dem Schlüsselwort ENTITY und dem Entity-Namen, wobei der Leerraum auf beiden Seiten davon notwendig ist. Dieses Parameter-Entity würde wie folgt benutzt werden:

 %node-decls; 

Beachten Sie, daß die Referenz auf das Parameter-Entity das Prozentzeichen statt eines Kaufmanns-Unds (&) benutzt, um den Anfang des Namens zu markieren. Das ist notwendig, da sich die beiden Namensmengen überlappen könnten.

Der Effekt der Entity-Ersetzung ist sehr ähnlich zum Gebrauch von allgemeinen Entities. Der Ersetzungstext ersetzt einfach die Entity-Referenz, wonach die Interpretation des Dokumenttyps mit dem modifizierten Text fortgesetzt wird.

Der Nutzen von Parameter-Entities ist dann am größten, wenn man mit modularisierten Dokumenttypen arbeitet, die mittels Parameter-Entities sorgfältig entworfene Erweiterungsmechanismen zur Verfügung stellen. Eine große DTD, z. B. der Industriestandard DocBook-DTD für die Software-Dokumentation, kann angepaßt werden, indem ein neuer Dokumenttyp erzeugt wird, der einfach einige Parameter-Entities definiert und dann die DocBook-Standarddefinition einliest. Da die in der Anpassungsebene definierten Entity-Deklarationen jene in DocBook überschreiben, kann mit diesem Mechanismus der spezifische Dokumenttyp für ganz spezielle Zwecke eines Projekts erweitert oder eingeschränkt werden.

Elementtyp-Deklarationen

Elementtyp-Deklarationen werden zur Einschränkung des Elementinhalts verwendet. Sie geben an, welche Elementtypen als Kindelemente benutzt werden können, und zeigen, wie die Kinder angeordnet werden dürfen. Elementtyp-Deklarationen können wie folgt aussehen:

<!ELEMENT br EMPTY>
<!ELEMENT generic ANY>
<!ELEMENT name (address+)>
<!ELEMENT para (#PCDATA | list | picture)*>

Wir können die Deklaration in spezielle syntaktische Komponenten aufbrechen, wobei jede Komponente einem speziellen Zweck dient:

 <!ELEMENT name content-model> 

Der Text <!ELEMENT teilt dem Parser mit, daß dies eine Elementtyp-Deklaration ist. name gibt dem Elementtyp einen Namen, mit dem er aus anderen Teilen der Dokumenttyp-Definition referenziert werden kann. content-model wird benutzt, um anzugeben, welchen Inhalt das Element haben darf, ob es Zeichendaten, andere Elemente oder beides enthalten darf. Kein Elementtyp darf mehrmals deklariert werden.

Interessanterweise gibt es keinen Platz für die Deklaration von Attributen. Obwohl Attribute mit Elementtypen assoziiert sind, werden sie mit Attributdeklarationen definiert, die später – im Abschnitt Attributdeklarationen – beschrieben werden.

Inhaltsmodelle

Ein Inhaltsmodell beschreibt, welche Elemente als Kinder des deklarierten Elementtyps erlaubt sind, wie ihre erlaubte Reihenfolge und Kombination aussieht und ob beliebige Zeichendaten erlaubt sind.

Die Inhaltsmodelle aller Elemente können in zwei Kategorien eingeteilt werden:

Element-Inhalt

Damit wird ein Inhalt beschrieben, der nur aus Elementen besteht. Das heißt, Sie definieren z. B. ein Adressenelement, das keine Zeichendaten, sondern nur Kindelemente benötigt. Die Spezifikation definiert Inhaltsteile, die »aus Namen, Auswahllisten von Inhaltsteilen oder Sequenzlisten von Inhaltsteilen bestehen«.

Gemischter Inhalt

Dieser Inhalt darf Zeichendaten enthalten. Dies ist die häufigste Anordnung in Textdokumenten:

<news title="XML aus dem Weltall">
  Dieser Artikel beschreibt XML-Übertragungen aus dem Weltall.
  <h1>Kein Meteor</h1>
  <para>Entgegen früherer Berichte ist das XML, das aus dem Weltall gelandet ist, kein Meteor.</para>
</news>

In diesem Beispiel kommen sowohl Elemente als auch Zeichendaten im news-Element vor. Elemente, die ein gemischtes Inhaltsmodell haben, müssen keine anderen Elemente als Inhalt zulassen. Tatsächlich darf ein Elementtyp nur mit Zeichendaten im Inhaltsmodell völlig leer bleiben. Es gibt keine Möglichkeit, um anzugeben, daß tatsächlich Zeichen in den Zeichendaten enthalten sein müssen.

Sehen wir uns unsere Beispiel-Elementdeklarationen noch einmal an:

 <!ELEMENT br EMPTY>

Diese Elementtyp-Deklarationen sind einfach. Mit dem Inhaltsmodell des ersten, EMPTY, kann ein leeres br-Element, wie man es in XHTML findet, beschrieben werden. Es darf keine Kindelemente und keine Zeichendaten enthalten. Es kann jedoch weiterhin nicht-inhaltliche Konstrukte enthalten, wie z. B. Kommentare oder Verarbeitungsanweisungen. Ein als EMPTY deklarierter Elementtyp wird als entarteter Spezialfall von Elementinhalten betrachtet.

 <!ELEMENT generic ANY>

Als nächstes haben wir ein Element namens generic, das jede Art von Elementen enthalten darf, die im Dokumenttyp definiert werden (das verbietet undefinierte Elementtypen!). Zusätzlich zu anderen Elementen sind auch Zeichendaten erlaubt, d. h., das Inhaltsmodell von ANY besteht aus gemischtem Inhalt.

 <!ELEMENT name (address+)> 

Das dritte Beispiel ist einfach, aber sehr verschieden von den anderen. Anstelle eines einfachen Namens wie ANY oder EMPTY wird das Modell durch etwas beschrieben, was einem regulären Ausdruck recht ähnlich sieht. In diesem konkreten Fall haben wir ein name-Element, das ein oder mehrere address-Elemente verlangt. Diese Art Inhaltsmodell ist vermutlich das meistverwendete und erlaubt detailliertere Angaben. Inhaltsmodelle können verschiedene Stufen der Komplexität erreichen, aber das Ziel ist immer das gleiche: den Inhalt zu definieren, der innerhalb des Elements erlaubt ist oder erwartet wird.

Das Inhaltsmodell wird mit runden Klammern und Kommas angegeben, um eine Sequenz zu bezeichnen. Ein vertikaler Strich (| ) gibt eine Auswahl an. Zum Beispiel:

 <!ELEMENT name (first, last)> 

Dieser Elementtyp verlangt ein Kindelement first, gefolgt von einem Kindelement last und nichts weiter. Wenn Sie entweder first oder last auswählen lassen wollen, benutzen Sie dazu einen vertikalen Strich:

 <!ELEMENT name (first | last)> 

Diese Ausdrücke können auch ineinander verschachtelt werden:

 <!ELEMENT order (sku, quantity, (account | name), price)> 

Das obige order-Element verlangt ein sku-Kindelement, gefolgt von einem quantity-Element, gefolgt von entweder einem account- oder einem name-Element, und abschließend gefolgt von einem price-Element.

Zusätzlich können die Operatoren +, * und ? am Ende von Inhaltsausdrücken angefügt werden, um die Anzahl anzugeben, in der ein Element oder eine Sequenz auftreten muß, oder um anzugeben, ob es wiederholbar ist oder überhaupt benötigt wird. Ohne solche Modifikatoren muß das Element genau einmal an dieser Stelle auftreten. Die Operatoren werden in der folgenden Liste erklärt:

+   Inhalt muß mindestens einmal oder mehrmals vorkommen.

*   Inhalt darf nicht oder mehrmals vorkommen.

?   Inhalt darf nicht oder einmal vorkommen.

Um z. B. ein order-Element mit genau einem account zu bekommen, dem mindestens ein oder mehrere sku- sowie ein oder mehrere price-Elemente folgen und das genau ein optionales ship-Element enthält, könnten Sie den folgenden Elementtyp verwenden:

 <!ELEMENT order (account, sku+, price+, ship?)> 

Um eine Kombination aus Zeichendaten und Elementen zu bekommen, können Sie den oder-Operator verwenden und Ihren gemischten Inhalt wie folgt definieren:

 <!ELEMENT paragraph (#PCDATA | list | picture)*> 

Dieser Elementtyp namens paragraph erlaubt wiederholte Sequenzen von Zeichendaten (angegeben durch den Stern), list-Elementen oder picture-Elementen innerhalb von paragraph-Elementen. #PCDATA kann nur mit Elementen kombiniert werden, die den oder-Operator in einer Gruppe benutzen, die einen *-Modifikator hat, und es kann nur in der äußersten geklammerten Gruppe eines Inhaltsmodells vorkommen.

Attributdeklarationen

Mit Attributen werden – wie bereits erläutert – Name/Wert-Kombinationen als Eigenschaften von Elementen angegeben. Attribute können nur in Start-Tags und leeren Element-Tags vorkommen. Eine Attributlistendeklaration wäre ein Teil einer DTD, mit der das XML-Dokument validiert wird. Es folgt ein Beispiel:

<!ATTLIST news
    title CDATA #REQUIRED
    author CDATA #IMPLIED>

Dies ist eine Attributlistendeklaration, die angibt, daß jedes news-Element ein title-Attribut bestehend aus Zeichendaten haben muß und optional ein author-Attribut haben darf, das ebenfalls aus Zeichendaten besteht.

Attributdatentypen

Die Spezifikation besagt, daß es drei verschiedene Arten von Attributtypen gibt: String, Token und Aufzählung. Im vorangegangenen Beispiel für Attributlisten haben Sie gesehen, daß ein news-Element ein title-Attribut mit dem Stringtyp CDATA benötigte.

Es gibt mehrere Token-Attributtypen:

ID

Ein eindeutiger Bezeichner für dieses Element. Der Bezeicher muß ein im aktuellen Dokument eindeutiger Name sein.

IDREF

Muß einer ID irgendwo im XML-Dokument entsprechen.

IDREFS

Eine Liste von einem oder mehreren mit Leerzeichen getrennten Namen. Jeder muß einer ID im Dokument entsprechen.

ENTITY

Entspricht dem Namen eines ungeparsten, im Dokument deklarierten Entity.

ENTITIES

Eine durch Leerzeichen getrennte Liste mit einem oder mehreren Entity-Namen.

NMTOKEN

Dieser am seltensten benutzte Typ entspricht einer NMTOKEN-Produktion, wie sie in der XML-Empfehlung definiert wird. Konsultieren Sie diese Empfehlung für weitere Informationen.

NMTOKENS

Eine durch Leerzeichen getrennte Liste mit einem oder mehreren NMTOKEN-Werten. Dies ist der am wenigsten benutzte Attributtyp.

Die verbleibenden Attributtypen, die Aufzählungstypen, werden in der Attributliste selbst definiert. Ein Aufzählungstyp ist ein Typ, der einen Namen aus einer definierten Liste von Namen nimmt, die in einer Attributdeklaration angegeben ist. Jede eigenständige Menge von Namen bildet einen eigenen Typ, der aber keinen Eigennamen hat. Ein Beispiel kann dies veranschaulichen:

 <!ATTLIST schiff typ (schaluppe | fregatte | schlauchboot) #IMPLIED> 

Diese Deklaration definiert einen Attributtyp, der einen der Werte schaluppe, fregatte oder schlauchboot enthält, aber keinen anderen Wert. Das Element <schiff typ="yacht"/> würde einen Validierungsfehler auslösen.

Attributwerte und -beschränkungen

Eine Attributdeklaration ermöglicht es dem Dokumenttyp, für ein Attribut einen Vorgabewert zu definieren, falls das Attribut nicht präsent ist. Sie kann auch angeben, ob das Attribut im Dokument weggelassen werden kann. Betrachten wir ein interessanteres Beispiel einer Attributdeklaration:

<!ATTLIST kapitel
     beschreibung  CDATA                       #IMPLIED     
     autor         CDATA                       #REQUIRED
     email         CDATA                       "info@example.com"
     version       CDATA                       #FIXED "1.0"
     typ           (normal|referenz|anhang)    "normal">

Das Attribut beschreibung muß ein String sein (CDATA), falls es angegeben wird, aber es wird nicht benötigt und hat keinen Vorgabewert, weil es als #IMPLIED angegeben wird. (In HTML werden die meisten Attribute auf diese Weise deklariert.) Die Einschränkung #REQUIRED bedeutet, daß das autor-Attribut im Dokument angegeben werden muß. Da es ein String ist, darf es auch leer sein. Wenn statt #IMPLIED oder #REQUIRED ein Stringwert deklariert wird, wie beim email-Attribut in unserer Beispiel-Attributliste, wird dieser zum Vorgabewert, der dann benutzt wird, wenn im Dokument kein Wert angegeben ist.

Die Einschränkung #FIXED kann nur zusammen mit einem Vorgabewert benutzt werden, was wir beim Attribut version sehen. Wenn diese Einschränkung benutzt wird, darf das Dokument das Attribut enthalten, aber der Wert muß exakt dem der Vorgabe entsprechen, obwohl er mit einer anderen Zusammensetzung von Zeichen, Entity- und Zeichenreferenzen codiert sein darf. Falls der Wert verschieden ist, gibt der Parser eine Fehlermeldung aus.

Das typ-Attribut ist ein Beispiel für einen Aufzählungstyp, ähnlich dem, den wir vorher gesehen haben. Vorgabewerte und Einschränkungen werden bei Aufzählungstypen genauso wie bei anderen Typen definiert. Es gibt nur eine zusätzliche Einschränkung: Wenn ein Wert angegeben wird, muß es ein in der Aufzählung enthaltener Name sein.

ID-Attribute verhalten sich speziell. Erzeugen wir einmal ein Attribut für das news-Element, das wir zuvor definiert haben:

<!ATTLIST news
      newsID ID #REQUIRED>

In dieser Attributliste müssen news-Elemente ein newsID-Attribut haben. Die erlaubten Werte werden von den Regeln des ID-Token-Typs bestimmt. Insbesondere ist der ID-Wert ein Name (wie im Abschnitt Namen definiert) und darf nicht mehr als einmal in einem XML-Dokument als Wert irgendeines Attributs vom Typ ID erscheinen. Mit anderen Worten: ID-Werte müssen ein Element innerhalb eines Dokuments eindeutig repräsentieren. Betrachten Sie ein erlaubtes Beispiel:

<news newsID="id39">Text</news>
<news newsID="id40">Text</news>

Da die Werte von ID-Attributen in einem Dokument eindeutig sein müssen, ist folgendes nicht erlaubt:

<news newsID="id39">Text</news>
<news newsID="id39">Text</news>

Außerdem darf bei keinem Element mehr als ein ID-Attribut definiert sein. Ein Elementtyp darf mehr als ein Attribut vom Typ ID definieren, für jedes Element darf höchstens ein ID-Wert definiert sein. Als Folge hiervon können einige Programmier-APIs die Werte von ID-Attributen dazu benutzen, um auf spezielle Elemente in einem Dokument zuzugreifen.

Am interessantesten bei ID-Attributen sind jedoch nicht die Attribute selbst, sondern der IDREF-Attributtyp. Während ein spezieller Wert nur einmal in einem Dokument als ID -Typ vorkommen darf, darf er beliebig oft als Wert eines IDREF- oder IDREFS-Attributs vorkommen. Insbesondere gilt, daß Attribute dieser Typen nur Werte annehmen können, die auch als Wert eines ID-Attributs irgendwo im gleichen Dokument vorkommen. (Ein IDREFS-Attribut kann einen Wert annehmen, der eine durch Leerzeichen getrennte Liste von ID-Werten ist, von denen jeder im Dokument existieren muß.) Diese Werte können dazu benutzt werden, interne Verbindungen zwischen Elementen herzustellen, die ein validierender Parser prüfen muß. Das kann dann sehr bequem sein, wenn eine einfache Baumstruktur nicht ausreicht, um Ihre Daten anzuordnen. Mit den ID-, IDREF- und IDREFS-Attributen kann das Datenmodell dahingehend erweitert werden, daß verbundene, gerichtete Graphen mit typisierten Kanten möglich sind.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema Python & XML bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2002 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "Python & XML" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O’Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de