Sql-und-Xml - Home

Xml lernen

Design-Entscheidungen zwischen Element und Attribut bei einer eigenen document type definition

Wollen Sie Ihre eigene document type definition entwickeln, so stellt sich rasch die Frage, welche Informationen Sie als Element und welche Sie als Attribut definieren wollen.

Abgrenzung unrelevanter Sachverhalte

Für diese Design-Frage sind alle möglichen Probleme der späteren Ausgabe irrelevant. Denn Sie können jede dieser Informationen mit XSLT auswerten, unabhängig davon, ob Sie die Information als Element oder als Attribut ablegen. Sie können sowohl mit <xsl:for-each select='...'> über Attribute als auch über Elemente Schleifenkonstruktionen laufen lassen, ebenso können Sie sich einzelne Werte gezielt mit <xsl:value-of select='...'/> ausgeben lassen.

Atomare Informationen

Liegt die abzulegende Information bereits atomar im Sinne der Datenbank-Normalisierung vor, so können Sie diese Information sowohl mit Elementen als auch mit Attributen codieren. Betrachten Sie die beiden folgenden Beispiele:
<artikel>
	<artikel-nummer>11</artikel-nummer>
	<artikel-name>Hose</artikel-name>
	<artikel-preis>39.90</artikel-preis>
</artikel>

<artikel artikel-nummer='11'
	artikel-name='Hose'
	artikel-preis='39.90' />
Im ersten Fall werden Elemente genutzt, wobei der Elementname gleich dem Spaltennamen ist. Im zweiten Fall wird als Attributname der Spaltenname verwendet. Offensichtlich benötigt die Version mit Elementen sehr viel mehr Speicher. Entfernt man alle hier zur besseren Übersicht gesetzten Tabulatoren, so benötigt die Element-Version 123 Byte, die Attribut-Version 73 Byte.

Gemischter Content

In allen Fällen, in welchen Text mit Unterelementen gemischt wird, benötigen Sie zwingend Unterelemente. Denken Sie an Texte, bei welchen einzelne Wörter hervorgehoben werden oder die Verweise enthalten. Denn in diesen Fällen sind Texte und Meta-Informationen, beginnend mit <, kombiniert, < darf jedoch nicht in einem Attribut-Wert verwendet werden.
<div>
	Dieser <b>Text</b> dient als Beispiel für einen
	<a name='thisSample' href='#thisSample'>Selbstverweis</a>.
</div>
Aufgrund der Beschränkung, daß der Ausdruck
<div value="Dieser <b>Text</b>" />
unzulässiges XML ist und Sie im Content von div weitere Elemente zu verwenden wünschen, müssen Sie für div ein Element nutzen. Sie können zwar durch
<div value="Dieser &lt;b&gt;Text&lt;/b&gt;" />
sich beim automatisch durchgeführten Ersetzen der Entities den obigen Text als Ausgabe erzeugen. Da dieser selbst nicht mehr bearbeitet wird, sind die dortigen Zeichen < und > nicht mehr als Meta-Informationen auswertbar.

Dieser gemischte Content, der später rekursiv ausgewertet werden soll, der also nach Unterelementen gescannt wird, so daß für jedes Unterelement selbst wieder ein Template ausgeführt wird, läßt sich mit dem XSLT-Element <xsl:apply-templates> auswerten. Der Parser sucht selbständig nach Unterelementen, identifiziert die zugeordneten Templates und wendet diese auf die Unterelemente an.

Spezielle Informationen, die nur einmalig oder eventuell mehrfach auftreten können

Das gesamte Beispiel können Sie direkt betrachten (xml-element-attribut-beispiel) oder es sich lokal kopieren.

Es gibt zusätzliche Informationen zu einem Element, die genau einmal existieren. Diese lassen sich am einfachsten durch ein Attribut codieren. Insbesondere können Sie dieses Attribut durch zusätzliche Bedingungen einschränken, die für Elemente nicht festgelegt werden können.
<person p-id='i-5' nachname='Maier' vorname='Fritz' geschlecht='m' />
Der zugehörige DTD-Abschnitt lautet:
<!ELEMENT person EMPTY>
<!ATTLIST person
	p-id ID #REQUIRED
	nachname CDATA #REQUIRED
	vorname CDATA #REQUIRED
	geschlecht (m | w) #REQUIRED >
Eine Person entspricht einem Element namens person und hat genau einen Nachnamen, einen Vornamen und ein Geschlecht. Definieren Sie ein Attribut vom Typ ID, so muß jeder Attribut-Wert in der Datei eindeutig sein. Da für das Attribut geschlecht nur zwei Werte sinnvoll sind, können diese in der Definition explizit angegeben werden, so daß ein validierender Parser Fehleingaben mit einem fatal error quittiert. Wäre der Wert von geschlecht als Element codiert, ließen sich die Werte nicht auf zwei Alternativen einschränken.

Sobald eine Zusatz-Information ein- oder mehrmals einer übergeordneten Information zugeordnet werden kann, sobald also im Datenbank-Sinn eine Detailtabelle benötigt wird, bei der es zu einer Id der Haupttabelle keinen, einen oder mehrere Detail-Einträge geben kann, benötigen Sie zwingend Unterelemente. Grund: Ein Attribut kann nur ein einziges Mal pro Element verwendet werden. Handelt es sich bei den Personen um Nutzer einer Bücherei und wollen Sie die ausgeliehenen Bücher pro Person codieren, so können Sie dies wie folgt tun:
<ausleihe p-ref='i-5'>
	<buch-ref b-ref='b-2' rueckgabe='1.10.2003'/>
	<buch-ref b-ref='b-7' rueckgabe='20.01.2004'/>
	<buch-ref b-ref='b-8' rueckgabe='Mayer macht Ferien'/>
</ausleihe>
Der zugeordnete DTD-Abschnitt lautet:
<!ELEMENT ausleihe (buch-ref*)>
<!ATTLIST ausleihe
	p-ref IDREF #REQUIRED>

<!ELEMENT buch-ref EMPTY>
<!ATTLIST buch-ref
	b-ref IDREF #REQUIRED
	rueckgabe CDATA #REQUIRED>
Die von einer Person ausgeliehenen Bücher sind als Unterelemente des Elements <ausleihe> gesammelt. Da für Personen und Bücher ID-Attribute definiert wurden, kann den Attributen p-ref bzw. b-ref die Eigenschaft IDREF zugewiesen werden. Damit dürfen als spätere Attributwerte ausschließlich ID's verwendet werden, die im Xml-Dokument existieren. Diese Darstellung entspricht einer eher objektorientierten Sichtweise, da die von einer Person ausgeliehenen Bücher zusammenhängend erfaßt sind.
Beachten Sie, daß die semantisch unsinnige Festlegung rueckgabe='Mayer macht Ferien' hier nicht vermieden werden kann. Xml kennt keine Datumstypen, so daß sich die Werte des Attributs @rueckgabe nicht auf gültige Datumsangaben einschränken lassen.

Eine eher relationale Sichtweise wird durch die folgende Definition hergestellt:
<!ELEMENT ausgeliehene-buecher (ausgeliehenes-buch*)>
<!ELEMENT ausgeliehenes-buch EMPTY>
<!ATTLIST ausgeliehenes-buch
	p-ref IDREF #REQUIRED
	b-ref IDREF #REQUIRED>

<ausgeliehene-buecher>
	<ausgeliehenes-buch p-ref='p-5' b-ref='b-2'/>
	<ausgeliehenes-buch p-ref='p-5' b-ref='b-7'/>
	<ausgeliehenes-buch p-ref='p-5' b-ref='b-8'/>
</ausgeliehene-buecher>
Hier markiert das einschließende Element <ausgeliehene-buecher> den Abschnitt, in dem alle ausgeliehenen Bücher zusammengefaßt wurden. Jedes Unter-Element <ausgeliehenes-buch> ordnet einer Person ein Buch zu. Dies entspricht einer Darstellung in einer relationalen Datenbank, in der <ausgeliehene-buecher> einer Tabelle entspricht und bei welcher die beiden Spalten p-ref und b-ref Fremdschlüssel auf die Tabellen Personen und Bücher enthalten.

Darstellung mit Elementen

Das obige Beispiel läßt sich auch folgendermaßen erfassen:
<ausleihe p-ref='i-5'>
	<buch-ref>b-2</buch-ref>
	<buch-ref>b-7</buch-ref>
	<buch-ref>b-8</buch-ref>
</ausleihe>
Der zugeordnete DTD-Abschnitt lautet:
<!ELEMENT ausleihe (buch-ref*)>
<!ATTLIST ausleihe
	p-ref IDREF #REQUIRED>

<!ELEMENT buch-ref (#PCDATA)>
Sie sehen an diesem Beispiel, daß Sie den Attribut-Wert auch als Element-Content notieren können, anstatt daß Sie dem Element ein Attribut zuordnen und das Element vom Typ EMPTY festlegen. Nur verlieren Sie damit die Möglichkeit, als zulässige Werte IDREF-Einträge zu fordern, so daß die Konsistenz des Xml-Dokuments bereits von einem validierenden Parser überprüft werden kann. Ferner können Sie diese Technik nur dann verwenden, wenn dem Element <buch-ref> kein weiterer Inhalt mehr zugewiesen werden muß, etwa weil es sich um einen atomaren Wert im Sinne einer normalisierten Tabelle handelt. Denn würden Sie nach demselben Muster dem Element <buch-ref> noch den weiteren Content 2003/10/12 als Ausleihdatum zuordnen, so wäre der gesamte Content etwa von der Form b-2 2003/10/12, so daß die Metainformation darüber, was Buchreferenz und was Datum ist, verlorengehen würde.

Vielleicht denken Sie an eine Darstellung derart, daß Sie die Redundanz zwischen Element und Attribut aufzuheben wünschen.
<ausleihe p-ref='i-5'>
	<b-2/>
	<b-7/>
	<b-8/>
</ausleihe>
Anstelle der Darstellung <buch-ref b-ref='b-2'/> wird hier nicht ein Element und ein Attribut verwendet, um einen Wert festzulegen. Stattdessen wird der Wert direkt als Elementname verwendet, diese weitaus kompaktere Darstellung scheint wünschenswert zu sein.

Der Nachteil dieser Lösung besteht darin, daß Sie hierfür keine statische DTD mehr festlegen können, da Ihnen die zulässigen Elementnamen nicht in Voraus bekannt sind. Ferner wird die Transformation eines so strukturierten Xml-Dokuments per XSLT unnötig kompliziert: Sie müßten den Elementnamen nach Textmustern durchsuchen und anhand dieses Suchergebnisses entscheiden, ob ein Wert in der Liste der Personen oder in der Liste der Bücher anzuzeigen ist. Verwenden Sie stattdessen eindeutige Element- und Attribut-Bezeichnungen, so können Sie für die Elemente eigenständige Templates erstellen oder diese mit <xsl:for-each> - Schleifen verarbeiten.

Ein Hinweis zum Beispiel

Das Beispiel verwendet für die Definition des Wurzel- oder Root-Elements den folgenden Ausdruck:
<!ELEMENT personen-und-buecher (person*, buch*, ausleihe*)>
Vielleicht wundern Sie sich, warum nicht stattdessen eine Definition wie folgt genutzt wurde:
<!ELEMENT personen-und-buecher
	(personen, buch-liste, alle-ausgeliehenen-buecher)>

<!ELEMENT personen (person*)>
<!ELEMENT buch-liste (buch*)>
<!ELEMENT alle-ausgeliehenen-buecher (ausleihe*)>
Beide Lösungen, sowohl die im Beispiel gewählte als auch die hier angegebene Definition, sind möglich. Unterschiede werden erst bei der Transformation beider Versionen deutlich. Bei der Beispiel-Datei ist die Hierarchie flacher, so daß mit einem XPath-Ausdruck "/personen-und-buecher/person" nach allen Personen gesucht wird, wohingegen die obige Definition nach "/personen-und-buecher/personen/person" sucht. Bei sehr großen Xml-Dokumenten könnte sich ein Leistungsunterschied zugunsten der mehrfach geschachtelten Lösung ergeben, da die Daten hier bereits vorstrukturiert sind.

© 2003-2017 Jürgen Auer, Berlin.