Auf XPath beruhende Identitätsprüfungen

(Auszug aus "XML Schema" von Eric van der Vlist)

Die IDs und IDREFs werden im PSVI in einer Tabelle namens »ID/IDREF-Tabelle« gespeichert und können bei Bedarf durch Anwendungen genutzt werden, um die entsprechenden Knoten aufzufinden. Wir können annehmen, daß XPath-Anwendungen (einschließlich XPointer) Abkürzungen und schnellen Zugriff auf die durch ein W3C XML Schema identifizierten Knoten bieten werden, wie es bei DTD-IDs bereits der Fall ist.

So einfach und gut benutzbar sie innerhalb ihres Bereichs auch sein mögen, behalten IDs und IDREFs doch die Beschränkungen ihrer DTD-Ahnen. W3C XML Schema bietet eine flexiblere Möglichkeit, Identitätsbedingungen ohne Einschränkungen für den lexikalischen Raum zu definieren. Dadurch werden lokale Schlüssel und Verweise ebenso möglich wie Multiknoten-Schlüssel.

Ein weiterer wichtiger Unterschied besteht darin, daß die ID/IDREF-Prüfungen auf Datentypen ausgeführt werden, die auf dem Typ xs:NMTOKEN beruhen, während die Prüfungen, die wir im Anschluß sehen werden, auf anderen Datentypen ausgeführt werden können. Dort finden die Vergleiche in den tatsächlichen Werteräumen statt, nicht mehr mit Hilfe der String-Darstellungen aus dem lexikalischen Raum. Diese Prüfungen beruhen auf einer Menge von XPath-Ausdrücken und werden durch drei unterschiedliche (aber ähnliche) Konstruktionen festgelegt, mit denen die Eindeutigkeit eines Werts geprüft oder ein Schlüssel bzw. ein Schlüsselverweis definiert wird.

Eindeutigkeit

Die erste dieser Konstruktionen definiert eine einfache Prüfung auf Eindeutigkeit. Wir werden für die genaue Erläuterung einige Zeit investieren, da die beiden anderen Konstruktionen dem gleichen Muster folgen.

Die Definition dieser Einschränkungen erfolgt über zwei aufeinanderfolgende relative XPath-Ausdrücke, die unter Verwendung der Position des Elements, unter dem sie definiert sind, ausgewertet werden. Wir brauchen eine klare Vorstellung von der Struktur der Instanzdokumente, um sie festzulegen. Der Ausgangspunkt ist der Ort des Elements, unterhalb dessen die Prüfung definiert wird. Dieser Ort bestimmt den Bereich der Prüfung und muß sorgfältig gewählt werden, denn er bildet die Grundlage, auf der alle Prüfungen für diese Einschränkung durchgeführt werden.

In unserer Bibliothek können wir uns beispielsweise dafür entscheiden, eine Prüfung für die Eindeutigkeit der ISBN der Bücher unterhalb des Elements library anzusiedeln, denn wir müssen diese Bedingung innerhalb des Bereichs der gesamten Bibliothek durchführen. Innerhalb eines Buchs können wir jedoch zusätzlich prüfen, daß der Verweis auf eine Figur innerhalb des Bereichs dieses Buchs eindeutig ist. Wir können diese zweite Prüfung innerhalb des Elements book definieren.

Nachdem wir einmal den Ort des Tests gewählt haben, beginnen wir damit, ihn am Ende der Definition des Elements aufzuschreiben:

<xs:element name="book" maxOccurs="unbounded">
    <xs:complexType>
        .../...
    </xs:complexType>
    <xs:unique name="book">
        .../...
    </xs:unique>
</xs:element>

Das hier verwendete Attribut name wird nützlich sein, wenn wir uns auf diese Einschränkung mit Hilfe eines keyref beziehen wollen.

Da wir nun den Namen und die Wurzel des Tests definiert haben, definieren wir den selector, der der relative Pfad des zu identifizierenden Knotens ist. Um in unserem Beispiel von library aus auf ein Buch zuzugreifen, lautet der relative Pfad book, und deswegen schreiben wir:

<xs:element name="library">
    <xs:complexType>
        .../...
    </xs:complexType>
    <xs:unique name="book">
        <xs:selector xpath="book"/>
        .../...
    </xs:unique>
</xs:element>

Wir haben die Forderung ausgedrückt, daß ein Buch innerhalb einer Bibliothek eindeutig sein soll. Um die Beschreibung dieser Prüfung zu vervollständigen, müssen wir durch field-Elemente definieren, wie ein Buch identifiziert wird.

In unserem Fall ist der Bezeichner das Unterelement isbn, und die vollständige Definition lautet:

<xs:element name="book" maxOccurs="unbounded">
    <xs:complexType>
        .../...
    </xs:complexType>
    <xs:unique name="book">
        <xs:selector xpath="book"/>
        <xs:field xpath="isbn"/>
    </xs:unique>
</xs:element>

In normales Deutsch übersetzt, liest sich diese Definition so: »In jeder Bibliothek sollte ein Buch, das über seine ISBN identifiziert wird, eindeutig bestimmt sein.«

Eine Eindeutigkeitsbedingung macht es nicht notwendig, daß der als Bezeichner verwendete Knoten (das »field«) angegeben werden muß. Selektoren, deren Feld nicht verfügbar ist, werden einfach ignoriert. Um dieselbe Prüfung mit einem notwendigen Feld zu definieren, sollte »key« (»Schlüssel«) statt »unique« (»eindeutig«) benutzt werden.

Zusammengesetzte Felder

Wenn die Namen der Autoren in unserer Bibliothek in Vornamen, mittleren Namensteil und Nachnamen aufgeteilt wären, fänden wir es wahrscheinlich praktisch, ein zusammengesetztes Feld zu definieren, das die Autoren identifiziert. W3C XML Schema ermöglicht dies durch die Definition mehrerer Felder innerhalb einer einzigen Bedingung – zum Beispiel:

<xs:element name="library">
    <xs:complexType>
        .../...
    </xs:complexType>
    <xs:unique name="author">
        <xs:selector xpath="author"/>
        <xs:field xpath="first-name"/>
        <xs:field xpath="middle-name"/>
        <xs:field xpath="last-name"/>
    </xs:unique>
</xs:element>

Die Prüfung wird dann auf dem Tripel ausgeführt, das aus den Werten der drei Felder (first-name, middle-name, last-name) besteht, die in ihrer Kombination eindeutig sein müssen.

Schlüssel

Ein Key (Schlüssel) ist eine Eindeutigkeitsbedingung mit der zusätzlichen Bedingung, daß alle als Felder genannten Knoten existieren müssen.

Die Schreibweise für die Definition eines Schlüssels ist die gleiche wie bei der Eindeutigkeitsbedingung, außer daß das Element unique durch das Element key ersetzt wird:

<xs:element name="library">
    <xs:complexType>
        .../...
    </xs:complexType>
    <xs:key name="book">
        <xs:selector xpath="book"/>
        <xs:field xpath="isbn"/>
    </xs:key>
</xs:element>

Offensichtlich gibt es eine Überlappung zwischen der zusätzlichen Existenzprüfung, die eine Schlüsselbeschränkung durchführt, und den anderen Methoden, die Anzahl der Vorkommen eines Elements oder eines Attributs zu steuern. Wenn in unserem Beispiel die Mindestzahl für das Auftreten von Autorennamen auf eins gesetzt ist, läuft es auf dasselbe hinaus, ob man nun xs:unique oder xs:key verwendet, außer wenn der Autorenname den Wert »nil« haben kann. (Wir werden den Wert »nil« unter Referenz auf Schemas und Schema-Datentypen in XML-Dokumenten besprechen.)

Schlüsselverweise

Ungeachtet seines Namens kann xs:keyref nicht nur zur Definition eines Verweises auf einen xs:key, sondern auch auf xs:unique verwendet werden.

Die Verwendung von xs:keyref ist ganz einfach und ähnelt der Verwendung von xs:key und xs:unique, wobei ein wichtiger Punkt erwähnt werden muß: Das Attribut refer von xs:keyref sollte auf ein Element xs:key oder xs:unique verweisen, das unterhalb desselben Elements oder eines seiner Nachkommen definiert ist.

Der Grund für diese Regel ist, daß die »Identitätsbeschränkungstabellen«, in denen die Schlüssel und die Verweise gespeichert werden, lokal innerhalb des Elements und seiner Ahnen vorliegen.

Die Definition der zueinander passenden xs:unique bzw. xs:key einerseits und xs:keyref andererseits müssen innerhalb desselben Elements stattfinden; anderenfalls hat einer der Ahnen Einfluß auf die Auswahl dieses Orts. Nehmen wir beispielsweise an, Bücher und Autoren würden sich in unterschiedlichen Abschnitten unseres Dokuments befinden.

<library>
    <books>
        <book>
            .../...
            <author-ref ref="Charles M. Schulz"/>
            .../...
        </book>
        .../...
    </books>
    <authors>
        <author>
            <name>
                Charles M. Schulz
            </name>
            .../...
        </author>
        .../...
    </authors>
</library>

Es ist gute Praxis, ein modulares Schema so aufzubauen, daß die Beschränkungen so nah wie möglich bei den Elementen, die sie betreffen, untergebracht sind. Dazu paßt es gut, einen Schlüssel in dem Element authors und den passenden keyref in dem Element books anzusiedeln. Da ein xs:keyref jedoch in demselben Element wie der passende xs:key oder aber in einem seiner Ahnen liegen muß und books kein Ahne von authors ist, kann die Definition von xs:keyref nur innerhalb des Elements library erfolgen. (Der xs:key kann entweder im Element library oder aber in authors liegen.)

Im vorigen Beispiel war es nur eine Frage des Stils, ob man xs:key in library oder aber in authors definiert, da die Autoren sowohl innerhalb einer library als auch innerhalb des Elements authors eindeutig waren. W3C XML Schema läßt jedoch auch Situationen zu, in denen dies nicht der Fall ist und in denen ein Schlüssel innerhalb eines Teildokumentbereichs eindeutig ist, nicht jedoch innerhalb des Gesamtdokuments.

Ändern wir das vorige Beispiel so ab, daß es mehrere Kategorien von Autoren gibt:

<library>
    <books>
        <book>
            .../...
            <author-ref ref="Charles M. Schulz"/>
            .../...
        </book>
        .../...
    </books>
    <authors>
        <category id="comics">
            <author>
                <name>
                    Charles M. Schulz
                </name>
                .../...
            </author>
            .../...
        </category>
        <category id="romane">
            .../...
        </category>
        .../...
    </authors>
</library>

Die Definition eines xs:key (oder eines xs:unique) innerhalb von library oder authors legt die Eindeutigkeit innerhalb des Bereichs der gesamten Bibliothek fest. Die Definition einer Autorenliste innerhalb von category legt hingegen nur die Eindeutigkeit innerhalb dieser Kategorie fest und läßt Autoren mit dem gleichen Namen zu, die in verschiedenen Kategorien definiert sind.

Im Sinne von W3C XML Schema ist es vollkommen zulässig, einen xs:key unter category und einen passenden xs:keyref unter library zu definieren (da library ein Ahne von category ist). Dadurch fügen wir den Autorennamen eine neue Beschränkung hinzu. Wenn ein Autor innerhalb eines Buchs referenziert wird, muß sein Name innerhalb des Bereichs von xs:keyref eindeutig sein. Angewendet auf unser Instanzdokument, bedeutet dies, daß »Charles M. Schulz« in mehreren Kategorien definiert sein kann, solange er in keinem Buch referenziert wird. Da er aber in einem Buch referenziert wird, darf sein Name nur einmal definiert sein.

Auch wenn dieses Verhalten in der Recommendation beschrieben ist, können die Ergebnisse für Schema-Designer überraschend sein. Wahrscheinlich ist es gute Praxis, die Definitionen von xs:key (bzw. xs:unique) und dem passenden xs:keyref im selben Element zu halten.

Zulässige XPath-Ausdrücke

Die Empfehlung zu W3C XML Schema legt für xs:selector und xs:field fest: »Um die Belastung für Implementationen, inbesondere für Streaming-Prozessoren, zu verringern, sind nur eingeschränkte Teilmengen von XPath-Ausdrücken erlaubt.« Das Ergebnis dieser Aussage ist eine begrenzte Teilmenge von XPath, die nur die Selektion von Knoten erlaubt, die Nachkommen des jeweiligen Orts oder aber Teil dieses Orts selbst sind.

Die XPath-Ausdrücke, die in xs:selector gestattet sind, dürfen ausschließlich tiefer in die Hierarchie der XML-Elementknoten hineingehen, erlauben keine Tests in den XPath-Schritten und müssen auf eine Menge von Elementen passen. Zusätzlich können die in xs:field erlaubten XPath-Ausdrücke auch Attribute selektieren.

Die vollständige BNF für diese Teilmenge ist im Referenzteil angegeben. Statt hier eine wortreiche Erklärung zu geben, sehen wir uns besser einige Beispiele für das mögliche und das unmögliche an.

Die folgenden Ausdrücke sind erlaubt:

xpath="author"

Wählt die Kindelemente namens author aus, die zu keinem Namensraum gehören.

xpath="author|character"

Wählt die Kindelemente namens author oder character aus, die zu keinem Namensraum gehören.

xpath="lib:author"

Wählt die Kindelemente namens author aus, die zu dem Namensraum gehören, dessen Präfix »lib« lautet.

xpath="*"

Wählt alle Kindelemente aus.

xpath="lib:*"

Wählt alle Kindelemente aus, die zu dem Namensraum gehören, dessen Präfix »lib« lautet.

xpath="authors/author"

Wählt alle Kindelemente authors/author aus.

xpath=".//author"

Wählt alle Elemente namens author aus, die Nachfahren des aktuellen Knotens sind und keinem Namensraum angehören.

xpath="author/@id"

Wählt das Attribut id des Kindelements author aus (nur für xs:field erlaubt, nicht aber für xs:selector).

xpath="@id|@name"

Wählt @id oder @name aus (gültig nur in xs:field, da Attribute in xs:selector verboten sind).

xpath="/library/author"

Absolute Pfade sind nicht erlaubt.

xpath="../author"

Die Elternachse ist nicht erlaubt.

xpath=".//*[@id]"

Tests sind nicht erlaubt.

xpath="author[@type='comics']"

Tests sind nicht erlaubt.

xpath="substring-after(@xlink:href,'#')"

Funktionsaufrufe sind nicht erlaubt.

xpath="//author"

Absolute Pfade sind nicht erlaubt.

Default-Namensräume werden innerhalb von XPath-Ausdrücken nicht angewendet, daher müssen Elemente und Attribute stets mit einem Präfix qualifiziert werden, wenn sie zu einem Namensraum gehören.

   

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XML Schema bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2003 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 Schema" 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