Sql-und-Xml - Home

Xml lernen

Namespace-Deklarationen in Kombination mit der Validierung bezüglich der externen DTD

Die im vorigen Abschnitt vorgestellten Namespace-Techniken genügen, sofern das Ziel nur in einer Ad-hoc-Zusammenführung mehrerer Dokumente besteht. Ferner können alle XSLT-Techniken mit dieser Konzeption arbeiten. XSLT kann bsp. für seine eigenen Elemente durchweg Namespace-Prefixes verwenden, damit gehören die Elemente ohne Präfix zu anderen Namespaces. Das Xml-Dokument kann auch zusätzliche Namespaces enthalten, dann werden in der XSLT-Datei Templates zu vollqualifizierten Namen definiert.

Die Grenzen dieser Technik werden sichtbar, sofern das aus mehreren eigenständigen Quellen mit eigenen DTD's zusammengefügte Xml-Dokument auf seine Validität gegen die Vereinigung der DTD's geprüft werden soll. Nun ist etwa das im vorigen Abschnitt verwendete Element 'title' in mehreren DTD's mit diesem Namen deklariert, beim Zusammenführen der DTD's über Parameter-Entities wird deshalb die Fehlermeldung ausgegeben: 'Das Element "title" ist bereits deklariert'. Die Zusammenführung der DTD's scheitert also, sobald zwei verschiedene DTD denselben lokalen Namen deklarieren. Jede DTD müßte sofort den qualified name, zusammengesetzt aus Präfix, Doppelpunkt und lokalem Namen definieren. Das Präfix wird jedoch erst im Xml-Dokument festgelegt, also wäre für jedes Präfix eine Kopie der DTD mit passendem Präfix zu erstellen.

Vielleicht vermuten Leser, daß bei der Deklaration eines Namespace unter Verwendung einer DTD als URI diese geladen und allen Elementen das gewählte Präfix zugewiesen wird. Beispiel:
<?xml version='1.0'?>
<!DOCTYPE b:my-root SYSTEM
	'http://www.sql-und-xml.de/xml-samples/books-small.dtd'>

<b:my-root
	xmlns:b='http://www.sql-und-xml.de/xml-samples/books-small.dtd'>
	<b:book>
	...
	</b:book>
</b:my-root>
Dies würde alle Elemente aus der Datei 'books-small.dtd' beim Laden dieser Datei dynamisch um das Präfix 'b:' ergänzen. Diese Vermutung ist falsch. Hintergrund all dieser Probleme ist, daß die Xml-Recommendation vom Februar 1998 stammt, die Festlegungen zum Namespace (Namespaces in XML - REC-xml-names) dagegen ein Jahr jünger sind. Die Xml-Recommendation, das normative Dokument zu Xml, kennt das Namespace-Konzept nicht.

Zunächst scheint dieses Problem unlösbar zu sein. Denn alle DTD's müßten in Abhängigkeit von den im Xml-Ergebnis-Dokument verwendeten Namespace-Prefixes umgeschrieben werden. Tatsächlich jedoch läßt sich dieses Problem mit Parameter-Entities in Kombination mit einer gemischten, externen und dokumentinternen DTD lösen.

Technischer Hinweis: Mozilla Firebird (0.7) scheint mit den globalen Entities, die im xmlns-Attribut verwendet werden, nicht klarzukommen (Bsp.: <books xmlns="&books.ns-value;">).

Hinweise zur Lösung

Die Lösung bedient sich zweier verschiedener Techniken. Zum einen werden die externen DTD's so umgeschrieben, daß die Elementdeklarationen mit Parameter-Entities durchgeführt werden, die dreiteilig sind: Präfix, Doppelpunkt und lokaler Elementname. Präfix und 'Doppelpunkt' können auch leer sein, dies entspricht der Verwendung der DTD ohne Namespace. Die Elemente werden hieraus nicht direkt erstellt, sondern es wird eine Parameter-Entity erstellt, welche alle Definitionen im Sinne eines langen Strings zusammenfaßt. Erst diese als String abgelegte Entity-Deklaration wird später ausgeführt. Eine dem Zieldokument zugeordnete DTD lädt diese anderweitig verwalteten DTD's und fügt sein eigenes Wurzelelement hinzu. Die Parameter-Entities, die das Namespace-Präfix eventuell neu festlegen, können zwar nicht im eigentlichen Xml-Dokument genutzt werden. Sie dürfen dort jedoch redefiniert werden und überschreiben damit den standardmäßig auf eine leere Zeichenfolge gesetzten Wert. Damit wird das Präfix nur im Ergebnis-Xml-Dokument festgelegt und die DTD erst aus diesen Informationen erstellt.

Für diese Technik ist keinerlei zusätzliche Programmiersprache notwendig, welche eine als Textdatei existierende DTD-Vorlage laden, alle Elemente mit Prefixes ergänzen und das Ergebnis als neue Datei erzeugen müßte. Dies gelingt ausschließlich mit den Xml-eigenen Werkzeugen. Denn der Ersetzungstext einer Entity kann selbst Deklarationen enthalten und gewisse Werte hierfür - die festgelegten neuen Prefix-Werte - sozusagen 'unmittelbar vor Ausführung' aus der internen Symboltabelle auslesen.

Code der umgeschriebenen DTD

Zunächst folgt der Code von einer der beiden umgeschriebenen DTD. Hier wird die books.dtd aufgeführt und durch zusätzliche Kommentare ergänzt:
<!-- Entities fuer das Prefix, den Trenner = Doppelpunkt,
	die Kombination aus beidem, die lokale Wurzel,
	die nach aussen sichtbare Wurzel und den Namespace -->


<!ENTITY % books.prefix "">
<!ENTITY % books.delimiter "">
<!ENTITY % bP "%books.prefix;%books.delimiter;">
<!ENTITY % books.local "books">
<!ENTITY % books.extern "%bP;%books.local;">

<!ENTITY % books.ns-value
	"http://www.sql-und-xml.de/xml-samples/books.dtd">
<!ENTITY books.ns-value "%books.ns-value;">

<!ENTITY % books.xmlns "xmlns:%books.prefix;">
<!ENTITY % books.xmlns.value "'%books.ns-value;'">
Hier werden zunächst diverse Entities definiert. Die beiden ersten können von außerhalb mit spezifischen Werten belegt werden. books.extern wird benötigt, um beim Dazwischenschieben einer weiteren DTD das Wurzelelement dieser DTD zur Verfügung zu stellen. books.ns-value stellt den Namespace-Wert dar. Dies ist sowohl als Parameter-Entity definiert, um später das Attribut mit diesem als Default-Wert zu deklarieren sowie als globale Entity, falls die Namespace-Deklaration in einem übergeordneten Element verwendet werden soll. Die Entity books.xmlns.value setzt den Wert von books.ns-value nochmals in einfache Hochkommata, da ansonsten die Auflösung keinen String ergeben würde.
<!-- Erzeugen aller Elemente / Attribute in einem Entity -->

<!ENTITY % books.execute '

<!ELEMENT %books.extern; (%bP;book*)>
<!ATTLIST %books.extern; %books.xmlns; CDATA %books.xmlns.value;>
<!ELEMENT %bP;book (%bP;title, %bP;author)>
<!ELEMENT %bP;title (#PCDATA)>
<!ELEMENT %bP;author (#PCDATA)>

'>

<!-- Ausfuehren der Entity -->

%books.execute;
Der erste Codeblock definiert eine mehrere Zeilen umfassende Parameter-Entity, die alle Elemente, gegebenenfalls auch Attribute deklariert. Ferner wird zum Wurzelelement das xmlns:Prefix - Attribut hinzugefügt und mit dem Standardwert deklariert. Dies ist notwendig, falls man die DTD eigenständig verwenden möchte. In diesem Fall gehört die Namespace-Deklaration in das Wurzelelement und muß, da die Validität des Xml-Dokumentes geprüft werden soll, zuvor definiert werden. Da in dem gesamten Ausdruck zunächst alle darin enthaltenen Paramter-Entities aufgelöst werden, kann vor jedes Element das aktuelle Präfix hinzugefügt werden, das als 'bP' definiert wurde. Die letzte Zeile führt dieses Entity schließlich aus.

Man kann in DTD's also ähnliche Techniken wie in anderen Programmiersprachen verwenden, bei welchen Befehlsfolgen - hier Entities - zur Laufzeit zusammengefügt und anschließend ausgeführt werden.

Diese DTD, so ungewöhnlich sie auf den ersten Blick aussehen mag, kann wie gewohnt mit einem Xml-Dokument verknüpft werden. Betrachten Sie die Datei books-with-default-namespace.xml:
<?xml version='1.0'?>

<!DOCTYPE books SYSTEM
  "http://www.sql-und-xml.de/xml-samples/books.dtd">

<books xmlns="&books.ns-value;">

	<book>
		<title>Reisen in Frankreich</title>
		<author>Mustermann, Max</author>
	</book>

</books>
Werden die Parameter-Entities nicht im Xml-Dokument redefiniert, so werden die Elemente einfach ohne Präfix genutzt. Es ist jedoch notwendig, den Standardnamespace zu deklarieren, da - zumindest bei der Verwendung des InternetExplorer 6 - ansonsten eine durchaus irreführende Fehlermeldung ausgegeben wird: 'Das Verwenden von Standard-Namespacedeklarationsattributen wird in DTD nicht unterstützt'. Fügt man das xmlns-Attribut explizit ein, entweder per Hand oder mit der vordefinierten globalen Entity, ist das Xml-Dokument valide.

Ebenso ist es möglich, dieselbe DTD mit Präfix zu verwenden. Das gewünschte Präfix wird in der internen DTD definiert, indem der Wert festgelegt wird. Dieser Abschnitt wird vom Parser zuerst ausgeführt. Alle in anschließend geladenen DTD's gefundenen weiteren Deklarationen für dieselbe Entity werden ignoriert, so daß der zuerst festgelegte Wert gültig ist. Dies ist in der Datei books-with-namespace-b.xml genutzt:
<?xml version='1.0'?>

<!DOCTYPE b:books SYSTEM
	"http://www.sql-und-xml.de/xml-samples/books.dtd"
[

<!ENTITY % books.prefix "b">
<!ENTITY % books.delimiter ":">


]>

<b:books xmlns:b='&books.ns-value;'>

    <b:book>
	<b:title>Urlaub am Mittelmeer</b:title>
	<b:author>Mustermann, Max</b:author>
    </b:book>

</b:books>
Völlig analog zur Datei books.dtd wurde eine Datei events.dtd erstellt, die - mit Ausnahme der Verwendung eines 'value'-Attributes für das Element 'title' ebenso aufgebaut ist. Ein Beispiel für den Default-Namespace liefert die Datei events-with-default-namespace.xml.

Zusammenführen der externen DTD's in einer DTD sowie ein passendes Xml-Dokument

Man mag sich vorstellen, daß bsp. verschiedene Abteilungen einer Firma DTD's nach dem bisherigen Schema erstellt haben. Eine Abteilung benötigt nun diese DTD's und soll mehrere Xml-Dokumente nicht nur zusammenführen, sondern auch auf ihre Validität testen. Dies läßt sich - nach den inzwischen erledigten Vorarbeiten - rasch durchführen.

Zunächst ist eine neue DTD zu erstellen - genannt events-and-books.dtd:
<!ENTITY % books.dtd SYSTEM
  "http://www.sql-und-xml.de/xml-samples/books.dtd">
<!ENTITY % events.dtd SYSTEM
  "http://www.sql-und-xml.de/xml-samples/events.dtd">

%books.dtd;
%events.dtd;

<!ENTITY % e-a-b.include "INCLUDE">

<![%e-a-b.include;[

<!ELEMENT events-and-books ((%events.extern;)*, (%books.extern;)*)>

<!ATTLIST events-and-books
	%events.xmlns; CDATA #FIXED %events.xmlns.value;
	%books.xmlns; CDATA #FIXED %books.xmlns.value;>

]]>
Diese DTD definiert die beiden bis dato geschriebenen DTD's als Parameter-Entities und lädt diese. Damit stehen die Entities für die Namen der Wurzelelemente (events.extern), die Namespace-Attribute (events.xmlns) und die Attributwerte (events.xmlns.value) zur Verfügung. Also kann das neue Wurzelelement statisch oder - wie hier gezeigt - dynamisch erzeugt werden. Das Xml-Dokument events-and-books.xml basiert auf dieser DTD:
<?xml version='1.0'?>

<!DOCTYPE events-and-books SYSTEM
  "http://www.sql-und-xml.de/xml-samples/events-and-books.dtd"
[

<!ENTITY % books.prefix "b">
<!ENTITY % books.delimiter ":">

<!ENTITY % events.prefix "e">
<!ENTITY % events.delimiter ":">

]
>

<events-and-books xmlns:b="&books.ns-value;"
	xmlns:e="&events.ns-value;">

<e:events>
    <e:event>
	<e:title e:value='Donnerstag-Runde'/>
	<e:room>K58</e:room>
    </e:event>
</e:events>

<b:books>
    <b:book>
	<b:title>Skifahren in den Alpen</b:title>
	<b:author>Musterfrau, Maria</b:author>
    </b:book>
</b:books>

</events-and-books>
Wenn man mag, kann man auch in der Datei books-with-namespace-b.xml den DTD-Abschnitt ausklammern, im obigen DOCTYPE-Abschnitt eine globale Entity hinzufügen, die auf dieses externe Xml-Dokument verweist und diese anstelle des unteren <books> - Abschnittes verwenden.
<!ENTITY books SYSTEM
  "http://www.sql-und-xml.de/xml-samples/books-with-namespace-b.xml">
Verwendung:
&books;
Dann wird das Xml-Dokument einer anderen Abteilung direkt vom dortigen Server schreibgeschützt eingelesen und mitsamt den anderen, auf anderen DTD's beruhenden Dokumentabschnitten validiert.

Ergänzende Hinweise zum Code sowie zur Verwendung

  • Die Pfade wurden immer absolut mit Verweis auf die hiesige Domain angegeben. Denn kopiert man sich den Code direkt in den Online-Xml-Trainer, um die Validität zu prüfen und die Wirkung einzelner Veränderungen zu testen, wird manchmal auf C:\Windows\System32 oder an anderen Stellen nach der DTD gesucht, diese jedoch nicht gefunden. Es ist folglich kaum einschätzbar und hängt von der lokalen Installation ab, was der InternetExplorer als Basisverzeichnis für die Ausführung von Code aus einer solchen Maske betrachtet. Auch das Kopieren sämtlicher Dateien in ein lokales Verzeichnis löst das Problem nicht. Um ein ständiges Anpassen auf lokale Pfade zu vermeiden, wurden diese absolut gesetzt.
  • Kopiert man sich das Beispiel in den Online-Xml-Trainer oder legt eine lokale Kopie an, so kann man aus dem Präfix 'b' auch ein 'c' oder etwas anderes machen - sowohl in der Entity-Definition als auch bei der Verwendung der Elemente. Das Ergebnis wird sofort valide sein, die DTD wird tatsächlich dynamisch zusammengebaut, ohne daß sie selbst das Präfix kennt. Einleuchtend ist, daß entweder das Präfix und der Doppelpunkt gesetzt sind oder daß beide Werte leere Zeichenfolgen enthalten. Ebenso offenkundig ist, daß für den Doppelpunkt kein anderes Zeichen verwendet werden darf. Die Trennung in zwei Entities ist notwendig, da das Präfix für die Attributdeklaration 'xmlns:Prefix' isoliert genutzt wird.
  • Wenn man das Beispiel nutzen möchte, so genügt es, die externen DTD's nach dem vorgestellten Muster zu gestalten. Der Block zu Beginn kann immer im wesentlichen unverändert übernommen werden, die Enitity- und Elementnamen sind anzupassen. Die eigentlichen Element- und Attribut-Typ-Deklarationen sehen wie gewohnt aus, sie werden von einfachen Hochkommata umschlossen. Das im Xml-Dokument verwendete Präfix wird in keiner externen DTD, sondern lediglich im Ergebnis-Xml-Dokument definiert und verwendet.
  • Ein einschränkender Hinweis: Enthalten die bisherigen DTD's Entity-Deklarationen mit übereinstimmendem Namen, so wird es zu Überlagerungen, damit zu Fehlern kommen. Eine Lösung besteht darin, zusätzliche Entity-Deklarationen mit in den dynamisch erzeugten Abschnitt zu übernehmen. Allerdings läßt sich als Präfix der Entity-Namen nicht der Doppelpunkt verwenden, hier kann bsp. das aktuelle Präfix mit '-' genutzt werden.

© 2003-2016 Jürgen Auer, Berlin.