XML-Dokumente mit XInclude modularisieren
14.
November 2008 02:06 von madmax
Wenn man umfangreiche XML-Dokumente schreibt, möchte man ungerne das vollständige Buch in einer einzigen Datei speichern. Bei einigen hundert Seiten wird es sonst sehr schnell unübersichtlich, und auch die Wiederverwendbarkeit der einzelnen Bestandteile (Topics) ist stark eingeschränkt. Aus diesem Grund gibt es in XML schon von Haus aus einen Mechanismus, mit dem man Teildokumente zu größeren Dokumenten zusammenführen kann: Entities. Mit der XInclude-Spezifikation steht jedoch noch ein sehr viel flexibleres Verfahren zur Verfügung, mit dem man auf Teile von XML- und auch Textdateien verweisen kann.
Wie funktioniert das eigentlich mit den Entities?
Das Einbinden von externen Dokumenten in ein XML-Haptdokument ist denkbar einfach. Es genügt, wenn man entsprechende Entities in der Dokumenttyp-Deklaration deklariert:
Man deklariert also Entitäten, die auf externe XML-Dokumente verweisen. Im Hauptdokument referenziert man anschließend die Dokumente, indem man an die Entity in der gewünschte Position aufruft:
Der Vorteil von Entities ist, dass es sich um einen Mechanismus handelt, der bereits im XML-Standard enthalten ist. Praktisch alle XML-Parser sollten in der Lage sein, auf diese Weise erstellte Dokumente zu lesen, richtig anzuzeigen und weiterzuverarbeiten. Dabei werden die externen Entitäten während des Parsens verarbeitet. Dabei einher geht auch die Validierung des Haupt- sowie aller referenzierten Teildokumente.
Ein Nachteil dieses Verfahrens ist, dass fehlende Teildokumente zu einem als schwerwiegend eingestuften Validierungsfehler (fatal error) führen. Die Weiterverarbeitung des Dokuments wird gestoppt. Für bestimmte Anwendungsfälle, etwa bei dynamisch generierten XML-Dokumenten, ist dieses Verhalten aber nicht immer gewünscht. Schließlich will man den Nutzern von Websites keine weißen Seiten präsentieren, weil vielleicht ein bestimmtes Teildokument nicht mehr vorhanden ist. In solchen Fällen würde es sich anbieten, wenn stattdessen ein Alternativdokument eingebunden werden würde, um zumindest die Teilausgabe des Hauptdokuments zu gewährleisten.
Ein weiteres Problem bei den Entitäten ist, dass sie an einer anderen Stelle deklariert werden müssen, bevor sie an der gewünschten Stelle im Dokument aufgerufen werden können. Dadurch muss man die Inklusion für ein Dokument an mehreren Stellen verwalten, was bei größeren Dokumenten den Wartungsaufwand erhöht. Zudem empfinden viele Autoren die DTD-Syntax als lästig, zumal sie sich von der XML-Syntax unterscheidet. Autoren müssen daher in ein und dem selben Dokument quasi zwei verschiedene Sprachkonstrukte verwalten, was ggf. ein wenig mühselig sein kann.
Wozu brauche ich XInclude?
XInclude bietet gegenüber dem Entities-Verfahren viel mehr Möglichkeiten:
Im href-Attribut wird einfach die URI zum einzubindenen Dokument angegeben. Zu beachten ist ferner, dass das XInclude-Element in einem eigenen Namespace (xi) mit entsprechender Namensraumdeklaration liegen muss, damit validierende Parser das Dokument auch korrekt verarbeiten können. Im Folgenden sei einmal das Beispieldokument abgebildet, auf das sich das obige Beispiel bezieht:
Wenn nicht das gesamte Dokument, sondern nur ein Teil davon inkludiert werden soll, kann man zusätzlich noch das XPointer-Attribut einsetzen:
Das XPointer-Attribut enthält dabei den Wert des ID-Attributs des einzubindenden Elements aus dem Zieldokument, das in href referenziert worden ist.
Aber was ist, wenn das einzubindende Dokument nicht vorhanden ist, weil es zum Beispiel aufgrund eines Fehlers nicht generiert worden ist? Zu diesem Zweck gibt es den Fallback-Mechanismus, mit dem alternative Dokumente eingebunden werden können:
Im obigen Beispiel wird in einem div eine Textdatei per XInclude eingebunden. Als zusätzliche XInclude-Anweisung dient das parse-Attribut, das dem XInclude-Prozessor den Typ der einzubindenden Ressource nennt. Erlaubt ist neben dem Standard "xml" auch "text". Im ersten xi:fallback-Element wird ein weiteres xi:include eingebettet, welches eine alternative Textdatei einbindet. Dieses eingebettete xi:include wird nur dann ausgewertet, wenn das erste xi:include scheitern sollte, weil beispielsweise die referenzierte Ressource nicht gelesen werden konnte. Das zweite eingebettete xi:fallback demonstriert schließlich, dass sich das Fallback-Element beliebig tief staffeln lässt. Im obigen Beispiel gibt das zweite Fallback-Element einen mailto-Link aus, falls auch das zweite xi:include scheitern sollte. Auf diese Weise wird gewährleistet, dass die Verarbeitung an dieser Stelle nicht abbricht, wenn weder die erste noch die zweite Ressource nicht eingebunden werden kann.
Stellt XInclude einen Ersatz für externe Entitys dar?
XInclude ist nicht dazu gedacht, Entity-Deklarationen zu ersetzen, sondern stellt eine zusätzliche, ergänzende Technologie dar. Dies ist schon mit den Unterschieden der beiden Technologien begründet, die bei der Verarbeitung von Dokumenten bestehen. Da externe Entitäten grundsätzlich zur Parse-Zeit verarbeitet werden, ist das resultierende Hauptdokument immer gültig. Beim XInlude-Verfahren kann das Dokument vor dem XInclude-Prozessorlauf ungültig sein, je nachdem, wie man das Dokument zusammengesetzt hat. Ggf. muss man die verwendete XML-Sprache wie DocBook erweitern, damit das XInclude-Element "erlaubt" wird.
Der Nachteil von XIncludes ist zudem, dass ein XML-Parser die Verarbeitung von XIncludes unterstützen muss, wenn man diese Funktion nutzen möchte. Häufig enthalten zwar Parser wie Xerces einen XInclude-Prozessor, dieser muss jedoch bei der Verarbeitung aktiviert werden. Dieser Punkt ist besonders dann wichtig, wenn man Dokumente später mittels XSLT transformieren möchte. Doch das ist eine andere Geschichte.
Wie funktioniert das eigentlich mit den Entities?
Das Einbinden von externen Dokumenten in ein XML-Haptdokument ist denkbar einfach. Es genügt, wenn man entsprechende Entities in der Dokumenttyp-Deklaration deklariert:
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"/usr/share/xml/docbook/schema/dtd/4.4/docbookx.dtd"[
<!ENTITY einleitung SYSTEM "Einleitung.xml">
<!ENTITY installation SYSTEM "Installation.xml">
...
<!ENTITY richTextEditor SYSTEM "RichTextEditor.xml"]> Man deklariert also Entitäten, die auf externe XML-Dokumente verweisen. Im Hauptdokument referenziert man anschließend die Dokumente, indem man an die Entity in der gewünschte Position aufruft:
<book lang="de">
<title>papaya CMS</title>
<subtitle>Handbuch für Redakteure </subtitle>
...
&einleitung;
... Der Vorteil von Entities ist, dass es sich um einen Mechanismus handelt, der bereits im XML-Standard enthalten ist. Praktisch alle XML-Parser sollten in der Lage sein, auf diese Weise erstellte Dokumente zu lesen, richtig anzuzeigen und weiterzuverarbeiten. Dabei werden die externen Entitäten während des Parsens verarbeitet. Dabei einher geht auch die Validierung des Haupt- sowie aller referenzierten Teildokumente.
Ein Nachteil dieses Verfahrens ist, dass fehlende Teildokumente zu einem als schwerwiegend eingestuften Validierungsfehler (fatal error) führen. Die Weiterverarbeitung des Dokuments wird gestoppt. Für bestimmte Anwendungsfälle, etwa bei dynamisch generierten XML-Dokumenten, ist dieses Verhalten aber nicht immer gewünscht. Schließlich will man den Nutzern von Websites keine weißen Seiten präsentieren, weil vielleicht ein bestimmtes Teildokument nicht mehr vorhanden ist. In solchen Fällen würde es sich anbieten, wenn stattdessen ein Alternativdokument eingebunden werden würde, um zumindest die Teilausgabe des Hauptdokuments zu gewährleisten.
Ein weiteres Problem bei den Entitäten ist, dass sie an einer anderen Stelle deklariert werden müssen, bevor sie an der gewünschten Stelle im Dokument aufgerufen werden können. Dadurch muss man die Inklusion für ein Dokument an mehreren Stellen verwalten, was bei größeren Dokumenten den Wartungsaufwand erhöht. Zudem empfinden viele Autoren die DTD-Syntax als lästig, zumal sie sich von der XML-Syntax unterscheidet. Autoren müssen daher in ein und dem selben Dokument quasi zwei verschiedene Sprachkonstrukte verwalten, was ggf. ein wenig mühselig sein kann.
Wozu brauche ich XInclude?
XInclude bietet gegenüber dem Entities-Verfahren viel mehr Möglichkeiten:
- Man kann auf vollständige XML-Dokumente als auch nur auf Teile davon verweisen.
- Es ist möglich, auf Textdokumente zu verweisen, um beispielsweise Quellcode in Listings einzulesen.
- Wenn eine externe Entity fehlt, führte das immer zu einem schwerwiegenden Fehler beim Verarbeiten. XIncludes erlauben es, bei fehlenden Verweisdokumenten auf alternative Dokumente zu verweisen.
<?xml version='1.0'?>
<preise>
<angefragt-von>Peter Schmitz</angefragt-von>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
href="preisliste.xml"/>
</preise> Im href-Attribut wird einfach die URI zum einzubindenen Dokument angegeben. Zu beachten ist ferner, dass das XInclude-Element in einem eigenen Namespace (xi) mit entsprechender Namensraumdeklaration liegen muss, damit validierende Parser das Dokument auch korrekt verarbeiten können. Im Folgenden sei einmal das Beispieldokument abgebildet, auf das sich das obige Beispiel bezieht:
<?xml version='1.0'?>
<!DOCTYPE preisliste SYSTEM "preisliste.dtd">
<preisliste xml:lang="de-de">
<element id="d001">
<beschreibung id="d001-beschreibung">
<p>Standard-Dingsbums</p>
</beschreibung>
<preise id="d001-preise">
<preis waehrung="EUR" anzahl="1+">39.95</preis>
<preis waehrung="EUR" anzahl="10+">34.95</preis>
<preis waehrung="EUR" anzahl="100+">29.95</preis>
</preise>
</element>
<element id="d002">
<beschreibung id="d002-beschreibung">
<p>Extragroßes Dingsbums mit Glöckchen <i>und</i> Pfeifen.</p>
</beschreibung>
<preise id="d002-preise">
<preis currency="EUR" anzahl="1+">59.95</preis>
<preis currency="EUR" anzahl="10+">54.95</preis>
<preis currency="EUR" anzahl="100+">49.95</preis>
</preise>
</element>
</preisliste> Wenn nicht das gesamte Dokument, sondern nur ein Teil davon inkludiert werden soll, kann man zusätzlich noch das XPointer-Attribut einsetzen:
<?xml version='1.0'?>
<preise>
<angefragt-von>Peter Schmitz</angefragt-von>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
href="preisliste.xml"
xpointer="d001-preise"/>
</preise> Das XPointer-Attribut enthält dabei den Wert des ID-Attributs des einzubindenden Elements aus dem Zieldokument, das in href referenziert worden ist.
Aber was ist, wenn das einzubindende Dokument nicht vorhanden ist, weil es zum Beispiel aufgrund eines Fehlers nicht generiert worden ist? Zu diesem Zweck gibt es den Fallback-Mechanismus, mit dem alternative Dokumente eingebunden werden können:
<div>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
parse="text"
href="beispiel.txt">
<xi:fallback>
<xi:include parse="text" href="alternativ-beispiel.txt"/>
<xi:fallback>
<a href="mailto:error@example.org">Fehler melden</a>
</xi:fallback>
</xi:include>
</xi:fallback>
</xi:include>
</div> Im obigen Beispiel wird in einem div eine Textdatei per XInclude eingebunden. Als zusätzliche XInclude-Anweisung dient das parse-Attribut, das dem XInclude-Prozessor den Typ der einzubindenden Ressource nennt. Erlaubt ist neben dem Standard "xml" auch "text". Im ersten xi:fallback-Element wird ein weiteres xi:include eingebettet, welches eine alternative Textdatei einbindet. Dieses eingebettete xi:include wird nur dann ausgewertet, wenn das erste xi:include scheitern sollte, weil beispielsweise die referenzierte Ressource nicht gelesen werden konnte. Das zweite eingebettete xi:fallback demonstriert schließlich, dass sich das Fallback-Element beliebig tief staffeln lässt. Im obigen Beispiel gibt das zweite Fallback-Element einen mailto-Link aus, falls auch das zweite xi:include scheitern sollte. Auf diese Weise wird gewährleistet, dass die Verarbeitung an dieser Stelle nicht abbricht, wenn weder die erste noch die zweite Ressource nicht eingebunden werden kann.
Stellt XInclude einen Ersatz für externe Entitys dar?
XInclude ist nicht dazu gedacht, Entity-Deklarationen zu ersetzen, sondern stellt eine zusätzliche, ergänzende Technologie dar. Dies ist schon mit den Unterschieden der beiden Technologien begründet, die bei der Verarbeitung von Dokumenten bestehen. Da externe Entitäten grundsätzlich zur Parse-Zeit verarbeitet werden, ist das resultierende Hauptdokument immer gültig. Beim XInlude-Verfahren kann das Dokument vor dem XInclude-Prozessorlauf ungültig sein, je nachdem, wie man das Dokument zusammengesetzt hat. Ggf. muss man die verwendete XML-Sprache wie DocBook erweitern, damit das XInclude-Element "erlaubt" wird.
Der Nachteil von XIncludes ist zudem, dass ein XML-Parser die Verarbeitung von XIncludes unterstützen muss, wenn man diese Funktion nutzen möchte. Häufig enthalten zwar Parser wie Xerces einen XInclude-Prozessor, dieser muss jedoch bei der Verarbeitung aktiviert werden. Dieser Punkt ist besonders dann wichtig, wenn man Dokumente später mittels XSLT transformieren möchte. Doch das ist eine andere Geschichte.
Posted in XML