In diesem Abschnitt diskutieren wir einige der häufigsten Probleme, denen sich der DTD-Designer gegenübergestellt sieht.
Benennung der
Elementtypen
Elementtypen:Benennung der Es mag auf den ersten Blick
seltsam erscheinen, dass wir hier die Namen der Elementtypen
diskutieren. Wie das Sprichtwort sagt, Namen sind Schall
und Rauch
. Aus Sicht des Computers ist das sicherlich
richtig; ihm ist es egal, welchen Namen ein Elementtyp
besitzt. Aber da immer noch Menschen die Texte schreiben,
ist es sinnvoll, sprechende
Namen zu
verwenden. Dies ist letzlich auch die Idee des Generic
Markup, die wir am Anfang des Buches als eine Motivation
genannt haben.
Für einfache, konkrete Faustregeln zur Bildung von Elementnamen machen wir folgende Vorschläge:
Was ist der Inhalt?im Gegensatz zur Frage
Wie sieht es aus?, die man eher mit einem Adjektiv beantwortet. Natürlich ist dies keine Garantie für eine gute Namenswahl. Auch
Fettdruckist ein Substantiv und bezieht sich ausschließlich auf das Aussehen.
Diese Regeln sind nicht für alle Anwendungen geeignet. Insbesondere bei Gliederungselementen befindet man sich häufig in einem Zweispalt. Wir wollen dies an folgenden Alternativen für die Benennung des ersten Kapitels eines Buches demonstrieren. Die erste Variante sieht so aus:
<buch> <einfuehrung> ... </einfuehrung> ... </buch>
Und hier folgt die zweite Fassung:
<buch> <kapitel> ... </kapitel> ... </buch>
Selbstverständlich enthält die erste Version mehr Informationen. Hier ist ganz klar, dass das erste Element im Buch die Einführung ist. Im zweiten Beispiel kann man nicht mehr darüber aussagen, als dass es sich um das erste Kapitel handelt, aber nicht welche Funktion es erfüllt.
Welche Lösung Sie in einem solchen Fall wählen sollten, hängt von Ihrer konkreten Anwendung ab. Können Sie auf die zusätzliche Information verzichten? Falls Sie aus all Ihren Texten die einführenden Worte extrahieren möchten, bekommen Sie im zweiten Beispiel Probleme. Denn dann muss ein menschlicher Leser nachsehen, ob das erste Kapitel wirklich eine Einleitung ist oder ob der Autor eine Einführung weglässt und sofort in medias res geht.
Auf der anderen Seite ist es natürlich leichter, in der DTD nicht auf alle Besonderheiten Rücksicht zu nehmen. In diesem Beispiel kann man noch einen Kompromiss eingehen. Dazu versehen wir den Elementtyp kapitel mit einem Attribut namens inhalt. Als mögliche Werte erlauben wir in der DTD einfuehrung und normal. Das obige Beispiel schreiben wir nun folgendermaßen:
<!-- In der DTD: --> <!ELEMENT kapitel ...> <!ATTLIST kapitel inhalt (normal|einfuehrung) "normal"> <!-- In der Instanz: --> <buch> <kapitel inhalt="einfuehrung"> ... </kapitel> <kapitel> ... </kapitel> ... </buch>
Auf diese Weise haben wir sowohl die Gliederungsinformation als auch die Information über den Inhalt. Der Aufzählungstyp des Attributs verhindert, dass ein Autor unvorhergesehene Begriffe eingibt (nur einfuehrung und normal sind erlaubt). Er lässt sich aber bei Bedarf leicht erweitern.
Wir sind damit schon bei der nächsten
philosophischen Frage angelangt: Soll ich Elemente oder
Attribute verwenden?
Elemente versus
Attribute
Elemente:versus
AttributeAttribute:versus Elemente
Meistens lassen sich — mit
mehr oder weniger großem Aufwand — Elemente und
Attribute ineinander umwandeln. Wir zeigen dies an einigen
Beispielen. Zuerst zwei Möglichkeiten, die Überschrift
eines Kapitels in einem Dokument unterzubringen:
<kapitel ueberschrift="Entwicklung einer DTD"> ... </kapitel> <kapitel> <ueberschrift>Entwicklung einer DTD</ueberschrift> ... </kapitel>
Die gewünschte Information ist in beiden Fällen vorhanden, wo liegen also die Unterschiede? — Attribute erlauben die Definition einer eingeschränkten Menge von erlaubten Werten. Dies ist hier sicherlich nicht sinnvoll. Andererseits können Sie für Elemente das Inhaltsmodell angeben; es können also eingebettete Elemente existieren. Dies geht bei Attributen nicht. Möchten Sie in Überschriften Auszeichnungen zulassen, so müssen Sie sich für das Element und gegen das Attribut entscheiden. In der folgenden Anwendung ist beispielsweise die DTD als acro ausgezeichnet, um den Inhalt als Akronym zu identifizeren.
<kapitel> <ueberschrift>Entwicklung einer <acro>DTD</acro></ueberschrift> ... </kapitel>
Als weiteren Grund für die Verwendung der Element-Variante in diesem Fall möchten wir anführen, dass die Überschrift ein Teil des Inhalts des Dokuments ist und als solcher in einem Element stehen sollte. Den anderen Fall zeigen wir mit einem weiteren Beispiel.
Hier geht es darum, eine Liste Ihrer Musiksammlung zu verwalten. Neben den Informationen wie Musiker, Band, Titel, Erscheinungsjahr und so weiter möchten Sie auch noch abgespeichern, auf welchem Aufnahmemedium, also Vinyl, CD oder MC, Sie die Aufnahme vorliegen haben. Wir zeigen auch hier wieder zwei Varianten, zunächst ausschließlich mit Elementen, dann mit einem Attribut.
<album> <medium>Vinyl</medium> <musiker>Jackson Browne</musiker> <titel>For Everyman</titel> <jahr>1973</jahr> <label>Asylum Records</label> ... </album> <album> <medium>CD</medium> <musiker>Jackson Browne</musiker> <titel>Running on Empty</titel> <jahr>1977</jahr> <label>Elektra/Asylum Records</label> ... </album>
Es folgt die Attribut-Alternative:
<album medium="Vinyl"> <musiker>Jackson Browne</musiker> <titel>For Everyman</titel> <jahr>1973</jahr> <label>Asylum Records</label> ... </album> <album medium="CD"> <musiker>Jackson Browne</musiker> <titel>Running on Empty</titel> <jahr>1977</jahr> <label>Elektra/Asylum Records</label> ... </album>
Hier ist die Information Vinyl
beziehungsweise
CD
kein eigentlicher Bestandteil des
Dokumentinhalts. Es handelt sich vielmehr um eine
Information über den Inhalt; es ist eine
Klassifizierung. In gleicher Weise könnten Sie auch festhalten, ob es
sich bei der Aufnahme um eine reguläre Veröffentlichung oder
um einen halboffiziellen
Konzertmitschnitt»Halboffiziell« klingt angenehmer
als »illegal«. handelt. Auch dies ist eine
Metainformation. Die Attributvariante erscheint deshalb in
diesen beiden Fällen angebracht. Ein weiteres Argument dafür
ist, dass hier nur eine begrenzte Anzahl von Möglichkeiten
existiert. Das Attribut sollten Sie daher folgendermaßen in
der DTD definieren:
<!-- Element Attributname Werte Vorgabe --> <!ATTLIST album medium (Vinyl| CD| MC) "Vinyl" >
Eine Einschränkung auf diese drei Werte ist bei einem Element nicht möglich.
Bisher haben wir einen Weg verschwiegen. Wir haben die Elemente, zuletzt medium, den anderen Elementen stets gleichgesetzt. XML erlaubt bekanntlich aber auch die Verschachtelung. Sie könnten Ihre Musiksammlung also auch so speichern:
<album> <vinyl> <musiker>Jackson Browne</musiker> <titel>For Everyman</titel> <jahr>1973</jahr> <label>Asylum Records</label> ... </vinyl> </album> <album> <cd> <musiker>Jackson Browne</musiker> <titel>Running on Empty</titel> <jahr>1977</jahr> <label>Elektra/Asylum Records</label> ... </cd> </album>
Hier ist die fragliche Information nun nicht mehr im Inhalt des Elements medium enthalten, sondern befindet sich im Namen der neuen Elemente vinyl, cd und mc. Dieser Ansatz bietet ähnliche Vorteile wie das Attribut: Zusammen mit folgender Definition des übergeordneten Elements album stehen auch hier nur die drei Wahlmöglichkeiten zur Verfügung.
<!ELEMENT album (vinyl | cd | mc) >
Wir favorisieren hier dennoch die Attribut-Variante, allerdings aus einem eher pragmatischen Grund heraus. Das Inhaltsmodell von vinyl, cd und mc ist identisch. Selbst wenn noch weitere Alternativen hinzukommen, etwa DVD oder Tonband, wird sich daran nichts ändern. Es besteht daher keine Notwendigkeit, drei Elementtypen zu verwenden, da sie alle gleich aufgebaut sind. Anders ist das, wenn die Elementtypen unterschiedliche Inhaltsmodelle besitzen. Dies wollen wir noch kurz erläutern, ohne jedoch ein Beispieldokument zu zeigen.
Eine Liste von Büchern, wie in der Bibliografie zu diesem Buch, ist sehr ähnlich zu obiger Musiksammlung. An die Stelle des Musikers tritt der Autor, das Label entspricht dem Verlag und so weiter. Das Aufnahmemedium entspricht ungefähr der Publikationsform, also Buch, Artikel, technischer Report, Doktorarbeit, Handbuch usw. Auch diese Information kann nun in der zuvor diskutierten Weise gespeichert werden. Im Gegensatz zu den Musikdaten empfiehlt sich hier jedoch nicht die Attributlösung, sondern die Verwendung des zuletzt gezeigten Ansatzes, der hier etwa so aussieht:
<!ELEMENT publikation ( buch | artikel | techreport | dokarbeit | handbuch ) >
Wir entscheiden uns für Elemente statt Attribute, weil jede dieser Alternativen jeweils ein anderes Inhaltsmodell besitzt. Zum Beispiel gehört zur bibliografischen Information eines Artikels die Angabe, in welcher Zeitschrift er erschienen ist. Bei einem Buch kommt diese Information nicht vor.
Wenn man nun eine Bibliografie aus einer Folge von Publikationen der obigen Form zusammensetzt, sieht das in der DTD wie folgt aus:
<!ELEMENT bibliografie (publikation+)>
Es ist naheliegend, den Elementtyp publikation einzusparen und ein Parameter-Entity daraus zu machen.
<!ENTITY % publikation "buch | artikel | techreport | dokarbeit | handbuch " > <!ELEMENT bibliografie (%publikation;)+>
Um die Diskussion zu beenden, fassen wir unsere Ausführungen in folgenden Tipps zusammen:
Gruppierung von Elementtypen mit
Parameter-Entities
Im Abschnitt 13.2.1 haben wir schon darauf
hingewiesen, dass es dem besseren Verständnis des Dokumenttyps dienen
kann, wenn Sie auf (implizit) vorhandene Hierarchien Ihrer Elemente achten. Die bei
dieser Analyse entstehenden Elementgruppen lassen sich
natürlich auch in der XML-DTD
darstellen. Der Schlüssel dazu sind die
Parameter-Entities. Aus technischer Sicht haben wir sie in
Abschnitt 5.8.3 schon behandelt. An
dieser Stelle möchten wir noch einmal den Zusammenhang mit
den Hierarchien aufzeigen. Dazu ziehen wir noch einmal die
Hierarchieebenen der Block- und der Zeichenelemente
heran. Wir gruppieren sie mit folgenden Parameter-Entities:
<!-- Parameter-Entities --> <!ENTITY % block "absatz | listing | abbildung | tabelle" > <!ENTITY % zeichen "acro | person | befehl | ort | strasse" > <!-- Hierarchie-Ebene Gliederung --> <!ELEMENT buch (kapitel+)> <!ELEMENT kapitel (ueberschrift, (%block;)*, abschnitt+)> <!ELEMENT abschnitt (ueberschrift, (%block;)*, unterabschnitt*)> <!ELEMENT unterabschnitt (ueberschrift, (%block;)+)> <!-- Hierarchie-Ebene Block --> <!ELEMENT absatz (#PCDATA | %zeichen;)* > <!ELEMENT ueberschrift (#PCDATA | %zeichen;)* > <!ELEMENT tabelle (#PCDATA | %zeichen;)* >
Wir haben hier auf eine Gruppierung der Gliederungsebene (Kapitel usw.) mit Parameter-Entities verzichtet, weil diese Elemente meist nur selten im Inhaltsmodell eines anderen Elementtyps vorkommen. Hingegen haben wir hier je dreimal die Abkürzungen %block; und %zeichen; eingesetzt. Neben der kürzeren Schreibweise und der verbesserten Lesbarkeit der DTD haben wir dadurch einen weiteren Vorteil erzielt: Änderungen der DTD sind leichter durchzuführen. Bei Einführung eines neuen Blocks beispielsweise, genügt die Änderung der Definition des Parameter-Entity %block;.
Weitere Erklärungen sollten überflüssig sein, da
dies letzlich nur eine Umsetzung von zuvor behandelten
Ideen ist. Eines scheint aber doch noch erwähnenswert: Die
Abbildung von Hierarchie-Ebenen auf Parameter-Entities ist
nicht eindeutigFür Mathematiker: Die Abbildung ist
nicht bijektiv :-): Die ueberschrift ist zwar sehr wohl ein
Blockelement, erscheint aber dennoch nicht im entsprechenden
Parameter-Entity. Der Grund ist einfach: Wir wollten
Überschriften nur am Anfang eines Kapitels, Abschnitts
usw. zulassen. Der Stern-Operator hinter %block;
hätte jedoch mehrere Überschriften erlaubt. Die Definition
von Parameter-Entities sollte also immer noch unter
pragmatischen Gesichtspunkten erfolgen.
Mehrfachverwendung von
DTD-Fragmenten versus
Spezialelemente
DTD:Fragmente
Bereits im Abschnitt 5.8.3 haben wir erwähnt, dass man
Elementtypen, die man in verschiedenen DTDs
benutzt, als Fragmente speichern kann und damit einzelne
Bausteine für eine neue DTD zur Verfügung
stehen. Diese Vorgehensweise ist auf der einen Seite sehr
praktisch, auf der anderen Seite können solche allgemeinen
Elementtypen niemals perfekt an die konkrete Anwendung
angepasst sein. Im Folgenden diskutieren wir diese
Problematik am Beispiel des Elementtyps Tabelle
und
zeigen einen Weg, der für einen geringen Preis alle Vorteile
besitzt.
Im folgenden Beispiel geht es darum, den
aktuellen Tabellenstand der Fußball-Bundesliga
aufzuschreiben. Auf den ersten Blick bietet es sich
an, dafür die DTD für eine Tabelle zu
verwenden. Wir gehen nun davon aus, dass ein Tabellenmodell
bereits verfügbar ist und dass es auch schon Formatierer bzw.
Konvertierer dafür gibt. Dann kann man gleich eine Instanz
schreibenBei
diesem Beispiel gehen wir davon aus, dass es sich bei der
Tabelle in der Datei
/usr/local/sgml/tabelle.dtd um ein
Modell handelt, das dem HTML-Tabellenmodell
entspricht.
Für den Inhalt zeichnen wir übrigens nicht
verantwortlich. Es ist ein Ausschnitt aus dem Tabellenstand
der Saison 1997/98 nach dem zwanzigsten
Spieltag.:
<?xml version="1.0"?> <!DOCTYPE table SYSTEM "/usr/local/sgml/tabelle.dtd"> <table border="1"> <tr><th>Mannschaft </th><th>Punkte</th><th>Tor-Verhältnis</th></tr> <tr><td>Kaiserslautern</td><td>45</td><td>+17</td></tr> <tr><td>B. München</td><td>41</td><td>+18</td></tr> ... <tr><td>VfL Bochum </td><td>20</td><td> -9</td></tr> <tr><td>1. FC Köln </td><td>20</td><td>-13</td></tr> </table>
Diese Instanz kann nun direkt formatiert oder anderweitig verarbeitet werden. In Abbildung 54 ist die Darstellung im Browser zu sehen. Zu verdanken ist dieser Vorteil der Mehrfachverwendung des existierenden Tabellenmodells.
Der Preis dafür ist, dass die benutzten Elementnamen
keine Aussage über den Inhalt machen. Ein
XML-System sieht
nur, dass es sich um
eine Tabelle handelt. Von der Fußball-Bundesliga hat es
keine Ahnung. Zugegeben, dem System etwas über Fußball
beizubringen, dürfte schwierig sein, aber es wäre doch
schöner, sprechende
Elementnamen zu verwenden, die
etwas über den darin befindlichen Inhalt aussagen. Ein
Modell dafür lässt sich leicht entwerfen. Wir zeigen es
gleich mit einer gültigen Instanz:
<?xml version="1.0"?> <!DOCTYPE liga [ <!ELEMENT liga (pos+) > <!ELEMENT pos (verein, punkte, tore) > <!ELEMENT verein (#PCDATA) > <!ELEMENT punkte (#PCDATA) > <!ELEMENT tore (#PCDATA) > ]> <liga> <pos> <verein>Kaiserslautern</verein> <punkte>45</punkte> <tore>+17</tore> </pos> <pos> <verein>B. München</verein> <punkte>41</punkte> <tore>+18</tore> </pos> ... <pos> <verein>VfL Bochum</verein> <punkte>20</punkte> <tore>-9</tore> </pos> <pos> <verein>1. FC Köln</verein> <punkte>20</punkte> <tore>-13</tore> </pos> </liga>
Nun hat man zwar den Vorteil, dass die Elemente eine
gewisse Meta-Information transportierenNebenbei
hat man noch etwas mehr gewonnen: Eine Position in der
Liga-Tabelle besteht aus den Elementen verein, punkte, tore. Ein guter
XML-Editor wird also für jedes Element pos automatisch die anderen drei
Elemente einfügen. Bei einer allgemeinen Tabelle kann er das
nicht, weil nicht klar ist, wie viele Felder eine
Tabellenzeile besitzt., jedoch
funktionieren die schon vorhandenen Formatierer nicht mehr,
denn sie kennen ja nur das allgemeine Tabellenmodell. Wir
beheben dieses Problem durch ein kurzes Jade-Programm,
welches das neue liga-Modell
in die alte table
transformiert.
<!DOCTYPE style-sheet PUBLIC "-//James Clark//DTD DSSSL Style Sheet//EN"> (declare-flow-object-class element "UNREGISTERED::James Clark//Flow Object Class::element") (element liga (make element gi: "table" (make element gi: "tr" ; Kopfzeile der Tabelle (make sequence (make element gi: "th" (literal "Mannschaft")) (make element gi: "th" (literal "Punkte")) (make element gi: "th" (literal "Tor-Verhältnis")))) (process-children))) ; Inhalt der Tabelle (element pos (make element gi: "tr")) (element verein (make element gi: "td")) (element punkte (make element gi: "td")) (element tore (make element gi: "td"))
Die Vorgehensweise lässt sich folgendermaßen zusammenfassen:
nahe stehen, bieten sich dafür an, da es leicht ist, wieder verwendbare Programme für die Formatierung zu schreiben.
sprechendeElementnamen, wenn Sie mit Ihren Daten vielleicht doch mehr anfangen möchten, als sie bloß auszugeben.