Validierung

(Auszug aus "XML in a Nutshell" von Elliotte Rusty Harold & W. Scott Means)

Ein gültiges Dokument enthält eine Dokumenttyp-Deklaration, die die DTD kennzeichnet, die für das Dokument ausschlaggebend ist. (Die Dokumenttyp-Deklaration und die Dokumenttyp-Definition sind zwei verschiedene Paar Schuhe. Die Abkürzung DTD wird korrekterweise nur verwendet, wenn die Dokumenttyp-Definition gemeint ist.)

Die DTD listet all die Elemente, Attribute und Entities auf, die das Dokument verwendet, sowie den jeweiligen Kontext, in dem sie benutzt werden. In der DTD können auch Einträge stehen, die das Dokument nicht benutzt. Die Gültigkeit beruht auf dem Prinzip, dass alles, was nicht erlaubt ist, verboten ist. Alles im Dokument muss einer Deklaration in der DTD entsprechen. Wenn ein Dokument eine Dokumenttyp-Deklaration hat und der DTD entspricht, auf die in dieser Deklaration verwiesen wird, wird es gültig genannt. Ist dies nicht der Fall, nennt man es ungültig.

Es gibt vieles, worüber die DTD nichts aussagt. Insbesondere sagt sie nicht,

  • wie das Wurzelelement des Dokuments aussieht,
  • wie viele Instanzen jedes einzelnen Elements im Dokument auftauchen,
  • wie die Zeichendaten innerhalb der Elemente aussehen und
  • was die semantische Bedeutung eines Elements ist, zum Beispiel, ob es ein Datum oder den Namen einer Person enthält.

DTDs erlauben Ihnen, der Form, die ein XML-Dokument annimmt, einige Beschränkungen aufzuerlegen; innerhalb dieser Grenzen gibt es allerdings weiterhin eine gewisse Flexibilität. Eine DTD sagt niemals etwas über die Länge, die Struktur, die Bedeutung, die erlaubten Werte oder andere Aspekte des Textinhalts eines Elements oder Attributs aus.

Gültigkeit ist optional. Ein Parser, der ein XML-Dokument liest, überprüft vielleicht die Gültigkeit, oder er verzichtet darauf. Falls er es tut, kann sich das Programm, das die Daten des Parsers entgegennimmt, um Gültigkeitsfehler kümmern oder auch nicht. In manchen Fällen, wenn es beispielsweise um Eingaben in eine Datenbank geht, kann ein Gültigkeitsfehler ein ernsthaftes Problem sein, wenn er z. B. anzeigt, dass ein benötigtes Feld fehlt. In anderen Fällen, etwa beim Zusammenstellen einer Webseite, ist ein Gültigkeitsfehler vielleicht nicht so wichtig, und ein Programm kann ihn ignorieren. Wohlgeformtheit ist in allen XML-Dokumenten notwendig, Gültigkeit nicht. Ihre Dokumente und Ihre Programme können eine Validierung einsetzen, ganz wie Sie das für notwendig erachten.

Ein einfaches DTD-Beispiel

Als Vorlage für das folgende Beispiel dient das Code-Beispiel Ein komplizierteres XML-Dokument, das eine Person beschreibt der Seite Elemente, Tags und Zeichen. Es beschrieb eine Person. Die Person hatte einen Namen und drei Berufe. Der Name bestand aus einem Vor- und einem Nachnamen. Bei der im Beispiel beschriebenen Person handelte es sich um Alan Turing. Das ist jedoch für DTDs nicht relevant. Eine DTD beschreibt nur den allgemeinen Typ, nicht die spezielle Instanz oder Ausprägung. Eine DTD für Personendokumente würde aussagen, dass ein Element person ein Kindelement name enthält, auf das null oder mehr beruf-Kindelemente folgen. Es würde außerdem besagen, dass das Element name genau ein Kindelement vorname besitzt, auf das genau ein Kindelement nachname folgt. Schließlich würde es festlegen, dass die Elemente vorname, nachname und beruf alle Text enthalten. Das folgende Beispiel ist eine DTD, die solch ein Element person beschreibt.

<!ELEMENT person (name, beruf*)>
<!ELEMENT name (vorname, nachname)>
<!ELEMENT vorname (#PCDATA)>
<!ELEMENT nachname (#PCDATA)>
<!ELEMENT beruf (#PCDATA)>

Code-Beispiel: Eine DTD für das Element person

Diese DTD würde vermutlich getrennt von den Dokumenten, die sie beschreibt, in einer Datei gespeichert werden. Dadurch kann aus mehreren XML-Dokumenten einfach auf sie verwiesen und zurückgegriffen werden. Falls das gewünscht ist, kann sie aber auch in das XML-Dokument eingebunden werden, indem die Dokumenttyp-Deklaration verwendet wird, die wir später in diesem Abschnitt noch behandeln werden. Wenn sie in einer separaten Datei gespeichert würde, erhielte diese wahrscheinlich den Namen person.dtd oder so ähnlich. Die Erweiterung .dtd wird standardmäßig verwendet, sie ist aber nicht explizit durch die XML-Spezifikation vorgeschrieben. Wenn ein Webserver diese Datei anbietet, dann erhält sie den MIME-Typ application/xml-dtd.

Jede Zeile aus dem Beispiel ist eine Element-Deklaration. Die erste Zeile deklariert das Element person; die zweite Zeile deklariert das Element name; die dritte Zeile deklariert das Element vorname – und so weiter. Die Zeilenumbrüche sind jedoch – außer aus Gründen der besseren Lesbarkeit – nicht relevant. Es ist zwar üblich, aber nicht notwendig, eine Deklaration auf eine eigene Zeile zu setzen. Lange Deklarationen können sogar über mehrere Zeilen gehen.

Die erste Element-Deklaration gibt an, dass jedes person-Element genau ein name-Kindelement enthalten muss, dem kein oder mehrere beruf-Elemente folgen. Das Sternchen hinter beruf bedeutet »kein oder mehrere«. Das heißt, jede Person muss einen Namen haben und kann keinen, einen oder mehrere Berufe besitzen. Der Name muss jedoch vor allen angegebenen Berufen stehen. Dieses person-Element ist z. B. gültig:

<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   <beruf>Informatiker</beruf>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
</person>

Dieses person-Element ist ebenfalls gültig, da das Element beruf als optional deklariert ist:

<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
</person>

Dieses person-Element dagegen ist nicht gültig, weil das erforderliche Kindelement name fehlt:

<person>
   <beruf>Informatiker</beruf>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
</person>

Dieses person-Element ist nicht gültig, weil ein beruf-Element vor name steht:

<person>
   <beruf>Informatiker</beruf>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
</person>

Das Element person darf keine Elemente enthalten, die nicht in seiner Deklaration aufgeführt sind. Die einzigen zusätzlichen Zeichendaten, die erlaubt sind, ist Whitespace. So ist zum Beispiel dieses person-Element ungültig, weil es ein zusätzliches veröffentlichungen-Element enthält:

<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
   <veröffentlichungen>On Computable Numbers...</veröffentlichungen>
</person>

Dies ist ein ungültiges person-Element, weil es außerhalb der erlaubten Kind-Elemente Text hinzufügt:

<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   war <beruf>Informatiker</beruf>,
   <beruf>Mathematiker</beruf> und
   <beruf>Kryptograph</beruf>
</person>

In all diesen Beispielen für ungültige Elemente könnten Sie die DTD ändern, um die Elemente gültig zu machen. Alle Beispiele sind trotz allem wohlgeformt. Sie sind jedoch im Sinne der DTD aus dem Code-Beispiel Eine DTD für das Element person nicht gültig.

Die Deklaration name besagt, dass jedes name-Element genau ein vorname-Element enthalten muss, dem genau ein nachname-Element folgt. Alle anderen Variationen sind verboten.

Die verbleibenden drei Deklarationen – vorname, nachname und beruf – legen fest, dass ihre Elemente #PCDATA enthalten müssen. Dieses DTD-Schlüsselwort steht für Parsed Character Data (geparste Zeichendaten) – das ist reiner Text, der möglicherweise Entity-Referenzen wie &amp; und &lt;, jedoch keine Tags oder Kind-Elemente enthält.

Das Code-Beispiel "Eine DTD für das Element person" hat die komplizierteste und höchste Deklaration an die Spitze gesetzt. Das ist jedoch nicht notwendig. Zum Beispiel zeigt das folgene Beispiel eine äquivalente DTD, die einfach die Reihenfolge der Deklarationen ändert. DTDs erlauben vorwärtsgerichtete, rückwärtsgerichtete und kreisförmige Referenzen auf andere Deklarationen.

<!ELEMENT vorname (#PCDATA)>
<!ELEMENT nachname (#PCDATA)>
<!ELEMENT beruf (#PCDATA)>
<!ELEMENT name (vorname, nachname)>
<!ELEMENT person (name, beruf*)>

Code-Beispiel: Eine alternative DTD für das Element person

Die Dokumenttyp-Deklaration

Ein gültiges Dokument enthält eine Referenz auf die DTD, mit der es verglichen werden soll. Diese Referenz wird in der Dokumenttyp-Deklaration des Dokuments angegeben. Eine Dokumenttyp-Deklaration sieht so aus:

 

<!DOCTYPE person SYSTEM "http://www.cafeconleche.org/dtds/person.dtd">

Sie gibt an, dass das Wurzelelement des Dokuments person ist und dass die DTD für dieses Dokument unter "http://www.cafeconleche.org/dtds/person.dtd" zu finden ist.

Die Deklaration des Dokumenttyps steht im Prolog (Vorspann) des XML-Dokuments hinter der XML-Deklaration, aber vor dem Wurzelelement. (Zum Prolog zählt alles im XML-Dokument vor dem Start-Tag des Wurzelelements.) Das folgende Beispiel demonstriert dies.

<?xml version="1.0" standalone="no"?>
<!DOCTYPE person SYSTEM "http://www.cafeconleche.org/dtds/person.dtd">
<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   <beruf>Informatiker</beruf>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
</person>

Befindet sich das Dokument am gleichen Bezugsort wie die DTD, können Sie eine relative URL anstelle der absoluten Form benutzen, z. B.:

 

<!DOCTYPE person SYSTEM "/dtds/person.dtd">

Es ist sogar möglich, nur den Dateinamen zu verwenden, falls sich die DTD im gleichen Verzeichnis befindet wie das Dokument:

 

<!DOCTYPE person SYSTEM "person.dtd">

PUBLIC-Identifier

Standard-DTDs können wirklich unter mehreren URLs gespeichert werden. Falls Sie am Strand zum Beispiel ein SVG-Bild auf Ihrem Laptop zeichnen, wollen Sie die Zeichnung vermutlich validieren, ohne erst eine Netzwerkverbindung zur Website des W3C herzustellen, wo sich die offizielle SVG-DTD befindet. Solche DTDs können mit PUBLIC-Identifiern verknüpft werden. Der Name des PUBLIC-Identifiers identifiziert die benutzte XML-Anwendung eindeutig. Gleichzeitig wird auch noch eine Reserve-URL angegeben, falls der Validator den PUBLIC-Identifier nicht erkennt. Um zu kennzeichnen, dass Sie einen PUBLIC-Identifier festlegen, benutzen Sie das Schlüsselwort PUBLIC anstelle von SYSTEM. Diese Dokumenttyp-Deklaration beispielsweise verweist auf die Rich Site Summary-(RSS-)DTD, die von Netscape standardisiert wurde:

<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd">

Ein lokaler Katalog-Server kann die öffentlichen IDs in die URLs umwandeln, die für die lokale Umgebung am passendsten sind. Die Kataloge selbst können in XML formuliert werden, im Speziellen z. B. im XML-Katalogformat von OASIS. In der Praxis werden PUBLIC-Identifier allerdings kaum verwendet. In der Regel beziehen sich validierende Parser auf die URL, um die Gültigkeit des Dokuments zu prüfen.

Interne DTD-Teilmengen

Wenn Sie das erste Mal eine DTD entwickeln, bietet es sich oft an, die DTD und ein vorschriftsmäßiges Beispieldokument in derselben Datei abzulegen, damit Sie sie gleichzeitig verändern und überprüfen können. Die Dokumenttyp-Deklaration könnte deshalb die DTD in eckigen Klammern enthalten, anstatt über eine externe URL auf sie zu verweisen. Das folgende Beispiel demonstriert dies.

<?xml version="1.0"?>
   <!DOCTYPE person [
   <!ELEMENT vorname (#PCDATA)>
   <!ELEMENT nachname (#PCDATA)>
   <!ELEMENT beruf (#PCDATA)>
   <!ELEMENT name (vorname, nachname)>
   <!ELEMENT person (name, beruf*)>
]>
<person>
   <name>
      <vorname>Alan</vorname>
      <nachname>Turing</nachname>
   </name>
   <beruf>Informatiker</beruf>
   <beruf>Mathematiker</beruf>
   <beruf>Kryptograph</beruf>
</person>

Code-Beispiel: Ein gültiges person-Dokument mit einer internen DTD

Manche Dokumenttyp-Deklarationen enthalten einige Deklarationen direkt, verweisen auf andere aber über einen SYSTEM- oder PUBLIC-Bezeichner. Diese Dokumenttyp-Deklaration beispielsweise deklariert die Elemente beruf und person selbst, verwendet jedoch die Datei name.dtd für die Deklaration des Elements name:

<!DOCTYPE person SYSTEM "name.dtd" [
   <!ELEMENT beruf (#PCDATA)>
   <!ELEMENT person (name, beruf*)>
]>

Der Teil der DTD innerhalb der eckigen Klammern wird interne DTD-Teilmenge genannt. Alle Teile, die ihren Ursprung außerhalb dieses Dokuments haben, werden externe DTD-Teilmenge genannt. Zusammen bilden sie die komplette DTD. Wichtig ist, dass die zwei unterschiedlichen Teilmengen kompatibel zueinander sein müssen. Die Element-Deklarationen einer Teil-DTD dürfen diejenigen der anderen nicht umdefinieren. Beispielsweise kann name.dtd das Element person nicht deklarieren, weil dieses bereits in der internen DTD-Teilmenge deklariert wird. Allerdings dürfen Entity-Deklarationen umdefiniert werden. Dies bringt einige wichtige Konsequenzen für die DTD-Struktur und den -Entwurf mit sich, wie wir in Kürze sehen werden, wenn wir uns mit Entities befassen.

Wenn Sie eine externe DTD-Teilmenge benutzen, sollten Sie für das Attribut standalone der XML-Deklaration den Wert no setzen:

 

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

Anmerkung: Tatsächlich gibt es in der XML-Spezifikation vier sehr ausführliche Regeln, wann genau bei Vorhandensein einer externen DTD-Teilmenge das Attribut standalone den Wert no haben sollte und wann nicht. Unterm Strich besagen diese Regeln jedoch einfach, dass fast alle XML-Dokumente, die externe DTD-Teilmengen benutzen, es erfordern, dass standalone auf den Wert no gesetzt wird. Da die standalone-Einstellung no immer erlaubt ist, selbst wenn sie nicht notwendig ist, lohnt es sich schlichtweg nicht, sich über die ungewöhnlichen Fälle Gedanken zu machen.

Ein validierender Prozessor muss die externe DTD-Teilmenge lesen. Ein nicht-validierendes Programm kann sie lesen, muss das aber nicht tun, selbst wenn standalone den Wert no hat. Das bedeutet, falls eine externe Teilmenge Deklarationen vornimmt, die Folgen für den Inhalt des Dokuments haben (zum Beispiel Standardwerte für Attribute vorgibt), hängt der Inhalt des Dokuments vom verwendeten Parser und seiner Konfiguration ab. Das hat bereits zu heilloser Verwirrung geführt. Obwohl einige der frühesten XML-Parser externe Entities nicht auflösten, können die meisten der noch in Benutzung befindlichen Parser das und tun es im Allgemeinen auch. Sie sollten die externe DTD-Teilmenge lesen, es sei denn, Ihr Hauptziel ist Effizienz oder Sie sind mit der Struktur der Dokumente, die Sie parsen, sehr vertraut.

Ein Dokument validieren

Im Allgemeinen gilt, dass Webbrowser Dokumente nicht validieren, sondern nur ihre Wohlgeformtheit überprüfen. Falls Sie Ihre eigenen Programme zum Verarbeiten von XML schreiben, können Sie die API des Parsers benutzen, um Dokumente zu validieren. Falls Sie die Dokumente von Hand schreiben und sie validieren wollen, können Sie entweder auf einen der online verfügbaren Validierer zurückgreifen oder ein lokales Programm ausführen, um die Gültigkeit des Dokuments zu überprüfen.

Die Online-Validierer stellen die vermutlich einfachste Möglichkeit zum Validieren Ihrer Dokumente dar. Zwei sind besonders erwähnenswert:

Zuerst müssen Sie das Dokument und die damit verknüpften DTDs auf einen öffentlich zugänglichen Webserver legen. Öffnen Sie als Nächstes die obige URL in einen neuen Tab, und geben Sie die URL des Dokuments, das Sie überprüfen wollen, in das Online-Formular ein. Der validierende Server übernimmt Ihr Dokument und teilt Ihnen mit, ob und welche Fehler er gefunden hat. Die folgende Abbildung zeigt die Ergebnisse des Brown-Validators für die Überprüfung eines einfachen ungültigen, aber wohlgeformten Dokuments.

Gültigkeitsfehler, entdeckt vom Online-Validierer der Brown University

Abbildung: Gültigkeitsfehler, die mit Hilfe des Online-Validierers der Brown University entdeckt wurden

Die meisten XML-Parser-Klassenbibliotheken enthalten ein einfaches Validierungsprogramm, das Sie benutzen können, wenn Sie wissen, wie Sie Kommandozeilen-Programme installieren und verwenden. Bei xmllint können Sie die Validierung mit dem Schalter --valid einschalten. (Standardmäßig prüft xmllint nur die Wohlgeformtheit.) Übergeben Sie dann auf der Kommandozeile die URLs oder Dateinamen der Dokumente, die Sie validieren wollen:

% xmllint --valid invalidhotcop.xml
invalidhotcop.xml:3: validity error: Element SONG content does not follow the DTD 
Expecting (TITLE , COMPOSER+ , PRODUCER* , PUBLISHER* , LENGTH? , YEAR? , ARTIST+),
got (TITLE PRODUCER PUBLISHER LENGTH YEAR ARTIST )
</SONG>
       ^

Anhand dieser Ausgabe können Sie erkennen, dass das Dokument invalidhotcop.xml in Zeile 3 einen Gültigkeitsfehler aufweist, der behoben werden muss.

Es gibt auch einige einfache GUI-Programme zum Validieren von XML-Dokumenten. Eines sehen Sie in der folgenden Abbildung: den Schematron-Validator von Topologi, der unter Windows läuft. Trotz seines Names kann dieses Produkt Dokumente gegen Schemas prüfen, die in den verschiedensten Sprachen geschrieben sind, seien es DTDs, RELAX NG oder die W3C-XML Schema-Sprache – und natürlich Schematron.

Gültigkeitsfehler, entdeckt vom Topologi-Schematron-Validator

Abbildung: Gültigkeitsfehler, die mit Hilfe des Schematron-Validators von Topologi entdeckt wurden

  

<< zurück vor >>

 

 

 

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

  


Copyright © 2005 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 "XML in a Nutshell" 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