Generelle Hinweise zur XML-Verarbeitung

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

Bei der Entwicklung einer neuen XML-Anwendung gibt es, wie bei jeder anderen Technologie auch, verschiedene Wege zur Umsetzung der meisten Designziele und natürlich auch einige Schwierigkeiten, über die man rechtzeitig informiert sein sollte. Kenntnisse darüber, wozu die neuen Funktionalitäten eingesetzt werden sollen, können dazu beitragen, dass die neue Anwendung nicht nur zu den jetzt schon bekannten Zielsystemen kompatibel ist, sondern auch mit anderen XML-Verarbeitungs-Tools zusammenarbeiten kann, die es möglicherweise noch gar nicht gibt.

What You Get Is Not What You Saw

Die XML-Spezifikation erlaubt einem XML-Parser gelegentlich einen gewissen Spielraum bei der Interpretation des Inhalts eines XML-Dokuments, sofern dabei der Sinn erhalten bleibt. Dieser Spielraum betrifft vor allem Kommentare, die einfach ausgelassen werden können, und Referenzen auf Entities, die der Parser stillschweigend, ohne die geringste Warnung an die Client-Anwendung, ersetzen kann. Die Rekonstruktion eines XML-Dokuments soll mit der gleichen logischen Struktur möglich sein, eine Übereinstimmung Byte für Byte ist im Allgemeinen dagegen nicht beabsichtigt.

Anmerkung: XML Canonicalization definiert eine konsistentere XML-Struktur und einen Prozess für ihre Erzeugung, die eine weitaus bessere Voraussage darüber erlauben, wie die Rekonstruktion eines Dokuments aus seinem logischen Modell aussehen wird.

Autoren von einfacheren XML-verarbeitenden Werkzeugen, die die Daten bei der Verarbeitung nicht speichern oder verändern, halten diese Einschränkungen vielleicht nicht für besonders hinderlich. Die Möglichkeit der exakten Rekonstruktion eines XML-Dokuments aus im Speicher gehaltenen Strukturen ist jedoch für Autoren von XML-Editoren und Content-Management-Lösungen ein kritischer Faktor. Auch wenn kein Parser gezwungen ist, Kommentare, ignorierbaren Whitespace und Referenzen auf Entities anzuzeigen, so beherrschen es dennoch viele oder können entsprechend konfiguriert werden.

Um sicherzustellen, dass ein Parser die Dokumente so ausgibt, wie Sie dies wünschen, und nicht nur den Minimalanforderungen der XML-Spezifikation genügt, bleibt Ihnen nichts anderes übrig, als die Dokumentation durchzulesen und den Parser entsprechend zu konfigurieren bzw. auszuwählen.

Die DTD lesen oder nicht lesen?

DTDs kommen in zwei Formen vor: intern und extern, und machmal auch in beiden Formen zugleich. Die XML-Spezifikation verlangt, dass alle Parser die interne DTD-Teilmenge lesen. Für eine Validierung muss die externe DTD-Teilmenge gelesen werden (wenn eine vorhanden ist). Erfolgt keine Validierung, ist dieser Schritt optional. Das Lesen der externen DTD-Teilmenge erfordert zusätzliche Zeit, insbesondere dann, wenn die DTD umfangreich ist und/oder auf einem Remote-Host im Netzwerk gespeichert ist. Deswegen sollten Sie eventuell darauf verzichten, sie zu laden, wenn Sie nicht validieren. Die meisten Parser bieten Optionen, über die angegeben werden kann, ob die externe DTD-Teilmenge und andere externe Entities aufgelöst werden sollen oder nicht. Würde die DTD nur zu Validierung verwendet, wäre die Entscheidung, ob die DTD geladen werden soll oder nicht, nicht so schwer. Unglücklicherweise ergänzen DTDs auch das Infoset des Dokuments um verschiedene wichtige Properties. Zu diesen zählen:

  • Entity-Definitionen
  • Vorgabewerte für Attribute
  • Die Angabe, ob Whitespace an Grenzen ignoriert werden kann

Da ein Dokument mit einer nicht-wohlgeformten DTD selbst ebenfalls nicht-wohlgeformt ist, kann eine DTD im Extremfall den Ausschlag darüber geben, ob ein Dokument lesbar ist oder nicht. Das bedeutet, dass das, was der Parser meldet, entscheidend davon beeinflusst werden kann, ob er die externe DTD-Teilmenge gelesen hat oder nicht. Um eine maximale Interoperabilität von Dokumenten zu gewährleisten, sollten Dokumente ohne externe DTD-Teilmengen angeboten werden. Dann ist das Verhalten des Parsers unabhängig von der Konfiguration vorhersehbar und reproduzierbar. Wenn Verbraucher von XML-Dokumenten sichergehen wollen, dass sie wirklich das empfangen, was der Sender beabsichtigt hat, sollten sie andererseits versuchen, eventuelle externe DTD-Teilmengen zu lesen, die das Dokument referenziert. Bei dem, was Sie versenden, sollten Sie konservativ sein (keine externen DTD-Teilmengen verwenden), und großzügig bei dem, was Sie empfangen (eventuelle externe DTD-Teilmengen für die empfangenen Dokumente lesen).

Whitespace

Die Art und Weise, wie Parser mit Whitespace umgehen, ist einer der am häufigsten falsch verstandenen Bereiche der XML-Verarbeitung. Es gibt vier grundlegende Regeln, die Sie sich merken sollten:

  1. Aller Whitespace in Elementinhalten wird grundsätzlich gemeldet.
  2. Whitespace in Attributwerten wird normalisiert.
  3. Whitespace in Prolog und Epilog und Tags, aber außerhalb von Attributen, wird nicht gemeldet.
  4. Alle nicht-geschützten Zeilenumbrüche (Wagenrücklauf, Zeilenvorschub, Wagenrücklauf/Zeilenvorschub-Paare und in XML 1.1 NEL und Unicode-Zeilentrenner) werden in Zeilenvorschübe umgewandelt.

Betrachten Sie das folgende Code-Beispiel:

<!DOCTYPE person  SYSTEM "person.dtd ">

<person  quelle="Alan Turing: Die Enigma,
                  Andrew Hodges, 1983">
  <name>
    <vorname>Alan</vorname>
    <nachname>Turing</nachname>
  </name>
  <beruf  id="p1"
               wert="Informatiker "
               quelle="" />
  <beruf  id="p2"
               wert="Mathematiker"/>
  <beruf  id="p3"
               wert="Kryptograph"/>
</person>

Wenn ein Parser dieses Dokument liest, meldet er allen Whitespace im Elementinhalt an die Client-Anwendung. Das schließt Whitespace an Grenzen wie den zwischen den Start-Tags <name> und <first> und den End-Tags </nachname> und </name> ein. Wenn die DTD festlegt, dass das Element name keine gemischten Inhalte enthalten kann, wird dieser Whitespace als Whitespace in Elementinhalten betrachtet, der auch als ignorierbarer Whitespace bezeichnet wird. Trotzdem wird er vom Parser immer noch gemeldet. Unabhängig davon, ob der Whitespace ignorierbar ist oder nicht, kann die Client-Anwendung, die den Inhalt vom Parser erhält, wählen, ob sie Whitespace an Grenzen ignorieren will und so tut, als solle er nur dazu dienen, die Darstellung des Dokuments zu verschönern. Der Parser meldet ihn grundsätzlich vollständig.

Zeilenumbrüche oder anderen Whitespace in Prolog und Epilog meldet der Parser nicht. Auch meldet er keine Zeilenumbrüche und keinen Whitespace in den Tags wie den zwischen den Attributen id und wert der beruf-Elemente. Ihr Programm sollte an keiner Stelle von derartigen Informationen abhängig sein.

Der Parser normalisiert Whitespace in Attributwerten. Im Minimalfall bedeutet das, dass er Zeilenumbrüche wie die in den quelle-Attributen in Leerzeichen umwandelt. Das ist alles, was passiert, wenn die DTD festlegt, dass das Attribut den Typ CDATA hat, oder das Attribut nicht deklariert oder die DTD nicht gelesen wurde oder nicht vorhanden ist. Wenn das Attribut jedoch irgendeinen anderen Typ wie ID oder NMTOKENS hat oder eine Aufzählung ist, entfernt der Parser allen führenden und anhängenden Whitespace aus dem Attribut und zieht alle verbleibenden Folgen von Whitespace-Zeichen zu jeweils einem einzigen Leerzeichen zusammen. Eine Normalisierung erfolgt jedoch nur bei literalem Whitespace. Leerzeichen, Tabulatoren und Wagenrückläufe, die in eine Zeichen- oder Entity-Referenz eingebettet sind, werden in den entsprechenden Ersetzungstext umgewandelt und dann erhalten. Anders als literaler Whitespace werden sie nicht normalisiert.

Entity-Referenzen

In XML-Instanzdokumenten gibt es drei Arten von Referenzen (zuzüglich eines anderen Paars in der DTD, das wir für den Augenblick ignorieren können):

  1. Numerische Zeichenreferenzen wie &#xA0; und &#160;
  2. Die fünf vordefinierten Entity-Referenzen &amp;, &gt;, &quot;, &apos; und &lt;
  3. Allgemeine Entity-Referenzen, die in der DTD definiert werden, wie &kapitel1; und &nbsp;

Die Verarbeitung der ersten beiden Arten ist kein Problem. Der Parser löst sie immer auf und sagt Ihnen nie etwas über sie. Als Parser-Client können Sie sie einfach ignorieren, und es wird das Richtige passieren. Der Parser meldet den Ersetzungstext genauso, wie er gewöhnlichen Text meldet. Er teilt Ihnen nicht einmal mit, dass diese Entity-Referenzen verwendet wurden. In seltenen Fällen können Sie eventuell ein spezielles Parser-Property setzen, mit dem Sie ihn veranlassen, derartige Dinge zu melden. Aber das werden Sie so gut wie nie einsetzen wollen. Es gibt nur einen Fall, in dem das Sinn macht: Wenn Sie einen XML-Editor schreiben, der versucht, die Quellform eines Dokuments zu umkurven.

Der dritte Fall ist etwas verzwickter. Diese Entity-Referenzen können auf externe Dateien auf entfernten Sites verweisen, zu denen Sie aus Gründen der Leistung, Verfügbarkeit und Sicherheit nicht grundsätzlich eine Verbindung herstellen wollen. Selbst wenn es interne Entities sind, können sie in der externen DTD-Teilmenge eines entfernten Dokuments definiert werden. Parser verhalten sich unterschiedlich dahingehend, ob sie derartige Entities standardmäßig laden sollen. Die meisten Parser und APIs bieten Möglichkeiten, um anzugeben, ob externe Entities geladen werden sollen oder nicht. Das ist aber nicht immer der Fall. XOM beispielsweise, löst externe Entities grundsätzlich auf. Der XML-Parser in Mozilla löst sie hingegen nie auf. Parser, die ein externes Entity nicht auflösen, sollten der Client-Anwendung allerdings melden, dass das Entity nicht geladen wurde – indem sie beispielsweise bei SAX skippedEntity( ) aufrufen oder bei DOM ein EntityReference-Objekt in den Baum einfügen. Wie das Programm auf derartige Meldungen reagiert, ist vom Kontext der jeweiligen Anwendung abhängig. Manchmal ist das ein fatales Problem. Manchmal ist es etwas, für das es einen Workaround gibt oder das man sogar ignorieren kann. Aber Sie sollten sich dessen bewusst sein, dass Sie diese Möglichkeit in Betracht ziehen müssen, wenn der Parser nicht so konfiguriert ist, dass er externe Entities grundsätzlich auflöst.

Anmerkung: In letzter Zeit haben sich einige Parser-Hersteller mit so genannten Billion Laugh-Angriffen befasst. Kurz gesagt funktioniert ein solcher Angriff, indem Entity-Referenzen definiert werden, deren Größe kontinuierlich verdoppelt wird. Besonders relevant ist das, wenn es in der internen DTD-Teilmenge erfolgt, in der die Entities aufgelöst werden müssen:

<!ENTITY ha1 "Ha! ">
<!ENTITY ha2 "&amp;ha1; &amp;ha1;">
<!ENTITY ha3 "&amp;ha2; &amp;ha2;">
<!ENTITY ha4 "&amp;ha3; &amp;ha3;">
<!ENTITY ha5 "&amp;ha4; &amp;ha4;">
<!ENTITY ha6 "&amp;ha5; &amp;ha5;">

...
<!ENTITY ha31 "&amp;ha30; &amp;ha30;">
<!ENTITY ha32 "&amp;ha31; &amp;ha31;">
...
<root>&amp;ha32;</root>

CDATA-Abschnitte

Die goldene Regel für den Umgang mit CDATA-Abschnitten ist folgende: Ignoriere sie! Wenn Sie Code zur Verarbeitung von XML schreiben, gehen Sie einfach davon aus, dass es keine CDATA-Abschnitte gibt. Dann wird alles ordentlich funktionieren. Der Inhalt von CDATA-Abschnitten ist einfacher Text. Er wird Ihrer Anwendung wie jeder andere Text als einfacher Text gemeldet, egal ob er in einen CDATA-Abschnitt eingeschlossen ist, durch eine Zeichenreferenz geschützt oder literal ausgeschrieben wurde, wenn ein Schützen nicht erforderlich ist. Beispielsweise sind diese beiden beispiel-Elemente in Bezug auf das, was Ihr Code wissen muss oder worum er sich kümmern muss, vollkommen identisch:

<beispiel><![CDATA[<?xml version="1.0"?>
<wurzel>
  Hallo!
</Wurzel>]]></beispiel>
<beispiel>&amp;lt;?xml version="1.0"?>
&amp;lt;wurzel>
  Hallo!
&amp;lt;/wurzel></beispiel>

Schreiben Sie keine Programme oder XML-Dokumente, bei denen es auf ihre Unterschiedlichkeit ankommt. Parser werden Ihnen den Unterschied nur selten (und nie zuverlässig) melden. Außerdem werden CDATA-Abschnitte oft vollständig entfernt, wenn Dokumente eine Verarbeitungskette durchlaufen. Dann bleibt der Inhalt zwar intakt, wird aber anders dargestellt – beispielsweise, wenn nicht serialisierbare Zeichen mit numerischen Zeichenreferenzen wiedergegeben werden. CDATA-Abschnitte sind eine kleinere Annehmlichkeit für menschliche Autoren, nicht mehr. Behandeln Sie sie nicht als Markup.

Das bedeutet auch, dass Sie nicht versuchen sollten, mit Hilfe von CDATA-Abschnitten ein XML-Dokument (oder HTML-Dokument) in ein anderes einzubetten. XML-Dokumente wurden nicht so entworfen, dass eines in das andere eingebettet wird. Die richtige Lösung für dieses Problem ist die Verwendung von Namensräumen: Mit ihnen können Sie festlegen, welches Markup was ist. Sie sollten nicht versuchen, ein Dokument als einen Umschlag für ein anderes zu verwenden. Setzen Sie CDATA-Abschnitte ebenfalls nicht ein, um nicht-wohlgeformtes Markup zu schützen wie das, das in vielen HTML-Systemen anzutrefen ist. Nutzen Sie stattdessen ein Tool, mit dem Sie nicht-wohlgeformtes HTML säubern können, bevor Sie es in ein XML-Dokument einbetten.

Kommentare

Obwohl es im Umgang mit HTML seit langem Tradition ist, Kommentare für Server-Side Includes (SSI) und zum Verstecken von JavaScript-Programmen und Cascading Stylesheets einzusetzen, ist es im Allgemeinen keine gute Idee, Kommentare für irgendetwas anderes als für von Menschen lesbare Anmerkungen zu verwenden. XML-Parser dürfen nämlich die Kommentare komplett ignorieren, und das geschieht auch häufig. Dadurch wird verhindert, dass die Kommentare überhaupt bei einer Anwendung ankommen. Transformationen ignorieren die Kommentare in der Regel ebenfalls.

Verarbeitungsanweisungen

XML-Parser müssen Client-Anwendungen den Zugriff auf XML-Verarbeitungsanweisungen ermöglichen. Diese erlauben es den Herstellern der Dokumente, mit XML-fähigen Anwendungen im Hintergrund so zu kommunizieren, dass es nicht zu Konflikten mit den Inhalten der Dokumente kommt. Sowohl bei der Validierung von DTDs als auch von Schemas werden Verarbeitungsanweisungen ignoriert. Daher ist es möglich, Verarbeitungsanweisungen irgendwo in der Dokumentstruktur zu verwenden und dabei die DTD oder das Schema nicht zu verändern. Die gebräuchlichste Anwendung von Verarbeitungsanweisungen ist die Einbettung von Referenzen auf Stylesheets in XML-Dokumenten. Ein Beispiel dafür sehen wir im folgenden XML-Fragment:

 

<?xml-stylesheet type="text/css" href="test.css"?>

Eine Anwendung, die den Umgang mit XML beherrscht, wie zum Beispiel der Internet Explorer 6.0, würde daraus lesen, dass der Autor des XML-Dokuments vorschlägt, das Stylesheet test.css zu verwenden, um den Inhalt darzustellen. Derartige Verarbeitungsanweisungen können auch Hinweise auf XSLT-Stylesheets oder sogar noch unbekannte Stylesheets geben. Allerdings muss die Client-Anwendung wissen, wie diese Art von Stylesheets zu verarbeiten ist, wenn das funktionieren soll. Anwendungen, die die Verarbeitungsanweisung nicht verstehen, wären aber nach wie vor in der Lage, das XML-Dokument zu lesen und seinen Inhalt zu verstehen, indem sie die Verarbeitungsanweisung einfach ignorieren.

Das Möbel-Beispiel, das wir auf der Seite Einige kommentierte Beispieldokumente der XML-Referenz (vgl. "Abbildung: buecherregal.xml") kennen lernen werden, enthält Verarbeitungsanweisungen, die für eine hypothetische Anwendung gedacht sind. Im XML-Dokument buecherregal.xml finden Sie eine Anweisung, die den Prozessor, der zur Verarbeitung des Beispiels eingesetzt wird, auffordert, die Liste der im XML-Dokument enthaltenen Teile mit der Liste der tatsächlich für das Möbelstück erforderlichen Teile zu vergleichen.

<teileliste>
        <teilename id="A" count="1">Rückwand</teilename>
        <teilename id="B" count="2">Seitenwand</teilename>
        <teilename id="C" count="1">Front</teilename>
        <teilename id="D" count="4">Brett</teilename>
        <teilename id="E" count="8">Versteckte Halterung</teilename>
        <teilename id="F" count="8">Halterungsschrauben</teilename>
        <teilename id="G" count="22">7/16" Muttern</teilename>
        <teilename id="H" count="16">Dübel</teilename>
    </teileliste>

<?MoebelPruefer    teileliste_pruefen?>

Wenn die Anwendung den Typ (das Ziel) der Verarbeitungsanweisung nicht kennt, wird sie als bedeutungslos angesehen.

Die XML-Spezifikation erlaubt es auch, eine Verbindung zwischen dem Ziel der Verarbeitungsanweisung, also dem XML-Namen unmittelbar nach dem <?, und einer Notation herzustellen. Allerdings ist das nicht wirklich erforderlich und wird auch selten benutzt.

Notationen

Die Notationssyntax von XML erlaubt dem Autor des Dokuments, den Typ eines externen ungeparsten Entitys innerhalb des XML-Frameworks anzugeben. Wenn eine Anwendung auf externe Daten zugreifen muss, die nicht in XML dargestellt werden können, sollten Sie einen Notationsnamen einführen und diesen dazu verwenden, externe ungeparste Entities zu definieren. Ein mögliches Beispiel einer solchen XML-Anwendung wäre ein XML-Dokument, das Java-Quelltext sowie einen Link auf den kompilierten Bytecode als externes ungeparstes Entity enthält.

Notationen stellen tatsächlich Metadaten dar, das heißt Kennzeichnungen der im Dokument enthaltenen Informationen. Um Notationen zu verwenden, muss man sie so in der DTD deklarieren, wie auf der Seite Externe ungeparste Entities und Notationen beschrieben. Eine Möglichkeit zur Verwendung von Notationen besteht darin, ein Attribut zur Angabe des NOTATION-Typs zu benutzen. Wenn ein Dokument zum Beispiel verschiedene Skripten enthält, die für verschiedene Umgebungen bestimmt sind, könnte man in dem Dokument einige Notationen und ein Attribut zur Angabe der Notation einführen. Mit dem zuvor eingeführten Attribut könnte man dann die Art des Skripts, das in einem Element enthalten ist, genauer beschreiben:

<!NOTATION DOS PUBLIC "-//MS/DOS Batch File/">
<!NOTATION BASH PUBLIC "-//UNIX/BASH Shell Script/">
<!ELEMENT batch-code (#PCDATA)*>
<!ATTLIST batch-code lang NOTATION (DOS | BASH)>
. . .
<batch-code lang="DOS">echo Hallo Welt!</batch-code>

Anwendungen, die mit dem PUBLIC-Identifier vertraut sind, wären je nach dem durch das Attribut lang angegebenen Typ in der Lage, den Inhalt des Elements korrekt zu interpretieren. (Notationen können auch mit SYSTEM-Identifiern umgehen, und Anwendungen können beide Arten von Bezeichnern korrekt behandeln.)

Eine weitere Anwendung von Notationen ist die Kategorisierung von Verarbeitungsanweisungen. Zum Beispiel könnten Sie die gerade eben gezeigte Verarbeitungsanweisung MoebelPruefer wie folgt in der DTD als Notation deklarieren:

 

<!NOTATION MoebelPruefer SYSTEM "http://namespaces.example.com/moebel">

Daran könnte das Programm zum Umgang mit Möbelstücken erkennen, dass die Verarbeitungsanweisung tatsächlich für dieses und nicht für irgendein anderes Programm mit einer gleichnamigen Verarbeitungsanweisung gedacht ist.

Ungeparste Entities

Ungeparste Entities verwenden eine Kombination aus Attributen und Notation-Deklarationen, um Verweise auf Inhalte herzustellen, die von der Anwendung auf noch unspezifizierte Weise weiter verarbeitet werden müssen. Allerdings wird diese Funktionalität selten von Anwendungen eingesetzt.

  

<< 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