Ersetzungsgruppen

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

In vielen Fällen muß ein Vokabular in der Lage sein, ganz verschiedene Inhaltsmodelle zu akzeptieren. Wir haben hierfür zwei Möglichkeiten: Wir können versuchen, einen einzigen generischen Elementnamen zu verwenden, oder wir können ein Schema definieren, das intelligent genug ist, mit dem möglichen Inhaltsmodell zurechtzukommen. Da wir (wegen der Regel von der konsistenten Deklaration) nicht mehrere verschiedene Inhaltsmodelle für dasselbe Element definieren können, haben wir entweder die Möglichkeit, xsi:type-Attribute in den Instanzdokumenten zu verwenden oder aber ein Inhaltsmodell zu definieren, das breit genug gehalten ist, um alle Möglichkeiten unterzubringen. Solch ein Modell wäre wahrscheinlich breit genug, auch unerwünschte Kombinationen zu akzeptieren.

Die einfachste Lösung, mit W3C XML Schema verschiedene Typen unterzubringen, besteht darin, für jeden Fall einen anderen Elementnamen zu verwenden. Wir haben bereits gesehen, daß der Kompositor xs:choice (außerhalb einer Gruppe) es erlaubt, Konstruktionen aufzubauen, bei denen ein Knoten in einem Instanzdokument ein aus einer Liste gewähltes Element akzeptiert. Diese Liste ist jedoch in der Definition des komplexen Typs festgeschrieben. Wir haben auch erörtert, daß diese Liste nicht erweitert werden kann, denn die Regeln für Ableitungen komplexer Typen durch Erweiterung lassen dies nicht zu. Ersetzungsgruppen bieten einen flexiblen Weg, einen Kompositor xs:choice (außerhalb einer Gruppe) aus einzelnen Elementen oder Verweisen zu konstruieren und ihn auch zu erweitern. Einfacher ausgedrückt, handelt es sich um Listen von Elementen, die untereinander austauschbar innerhalb von Dokumenten verwendet werden können. Eine wichtige Einschränkung soll jedoch erwähnt werden, bevor wir beginnen: Ersetzungsgruppen sind nur bei globalen Elementen anwendbar.

Ersetzungsgruppen können als erweiterbare Elementgruppen angesehen werden. Bevor wir sie besprechen, sehen wir uns noch einmal die »traditionellen« Elementgruppen an, damit wir die Unterschiede zwischen diesen beiden Begriffen hervorheben können. Da die Empfehlung in Bezug auf die Erweiterbarkeit von Elementgruppen und die Beschränkung von Ersetzungsgruppen besonders unscharf ist, habe ich es vorgezogen, eine konservative Interpretation zu geben, die keine Interoperabilitätsprobleme aufwerfen sollte. Ich werde die verschiedenen Interpretationen am Ende dieses Kapitels erörtern.

Verwendung einer »traditionellen« Gruppe

Kehren wir zu Definition eines Namens zurück. (Schließlich sind universelle Namen eines der kontroversesten Themen im Bereich der Normalisierung. Daher ist es keine Überraschung, daß sie gute Beispiele abgeben.) Statt mit Datentypen zu spielen, können wir einfach verschiedene Elementnamen verwenden. Wir können dann sagen, daß ein Name entweder ein einfacher Name ist wie

<simple-name>
   Snoopy
</simple-name>

oder aber ein vollständiger Name wie

<full-name>
   <last>
      Schulz
   </last>
   <first>
      Charles
   </first>
   <middle>
      M.
   </middle>
</full-name>

Wir wissen bereits, wie wir ein flexibles Schema definieren können, das auf diese beiden Dokumente paßt. Es ist eine gute Idee, eine Gruppe mit einem xs:choice-Kompositor zu erzeugen, der eines dieser beiden Elemente zuläßt und in sämtlichen Elementen wiederverwendet werden kann, in denen ein Name angegeben werden muß. Die logischen Schritte sind die Definition der beiden Elemente (full-name und simple- name), die Erzeugung einer Gruppe und die Verwendung in der Definition der Elemente author und character:

<xs:element name="full-name">
   <xs:complexType>
      <xs:all>
         <xs:element name="first" type="string32" minOccurs="0"/>
         <xs:element name="middle" type="string32" minOccurs="0"/>
         <xs:element name="last" type="string32"/>
      </xs:all>
   </xs:complexType>
</xs:element>
<xs:element name="simple-name" type="string32"/>
<xs:group name="name">
   <xs:choice>
      <xs:element ref="simple-name"/>
      <xs:element ref="full-name"/>
   </xs:choice>
</xs:group>
<xs:element name="author">
   <xs:complexType>
      <xs:sequence>
         <xs:group ref="name"/>
         <xs:element ref="born"/>
         <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
   </xs:complexType>
</xs:element>
<xs:element name="character">
   <xs:complexType>
      <xs:sequence>
         <xs:group ref="name"/>
         <xs:element ref="born"/>
         <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
   </xs:complexType>
</xs:element>

Wir können in diesem Fall xs:all verwenden, weil die betroffenen Elemente in dem Element full-name isoliert sind. An dieser Stelle sollte auch darauf hingewiesen werden, daß xs:all nicht bedeutet, daß es auf die Reihenfolge nicht ankommt, sondern nur, daß alle Kombinationen gültig sind. In diesem Fall kann

<full-name>
   <first>
      Eric
   </first>
   <last>
      van der Vlist
   </last>
</full-name>

bzw.

<full-name>
   <last>
      van der Vlist
   </last>
   <first>
      Eric
   </first>
</full-name>

vielleicht ausdrücken, ob ich es vorziehe, »Eric van der Vlist« oder aber »van der Vlist Eric« genannt zu werden. Anwendungen, die Zugang zu den Komponenten dieses full-namehaben wollen, können ihn problemlos haben, nur müssen diejenigen, die einen full-namebrauchen, die Reihenfolge im Dokument respektieren.

Ersetzungsgruppen

Verwendung von Ersetzungsgruppen

Wie können wir nun dasselbe Inhaltsmodell mit Hilfe von Ersetzungsgruppen definieren? Das erste, was zu tun ist, ist die Definition eines Elements, von dem sowohl full-name als auch simple-name abgeleitet werden kann. In diesem Fall haben wir einerseits einen einfachen Typ, andererseits einen komplexen Typ komplexen Inhalts. Daher können wir keinen Typ finden, der zu beiden erweitert werden kann. Wir haben keine Wahl, wir müssen mit dem universellen Typ beginnen, der jedes Inhaltsmodell akzeptiert. Dieser ganz besondere Typ namens xs:anyType ist auch der Standardwert, wenn kein Typ angegeben ist. Wir können ein generisches name-Element definieren, ohne irgendeine Typdefinition zu geben, um es so offen wie möglich zu halten:

<xs:element name="name"/>

Dieses Element wird zu dem werden, was man den Kopf der Ersetzungsgruppe nennt. Ohne in diesem Kopfelement etwas weiteres anzugeben, können andere Elemente erklären, daß sie überall dort verwendet werden können, wo das Kopfelement in dem Schema referenziert wird. Diese Elemente werden Mitglieder der Ersetzungsgruppe genannt. Die einzige Einschränkung für die Mitglieder liegt darin, daß ihre Typen gültige Ableitungen des Kopfelementtyps sein müssen. Diese Erklärung wird mit einem substitutionGroup-Attribut abgegeben, das in jedem austauschbaren Element das Kopfelement referenziert – beispielsweise so:

<xs:element name="simple-name" type="string32" substitutionGroup="name"/>
<xs:element name="full-name" substitutionGroup="name">
   <xs:complexType>
      <xs:all>
         <xs:element name="first" type="string32" minOccurs="0"/>
         <xs:element name="middle" type="string32" minOccurs="0"/>
         <xs:element name="last" type="string32"/>
      </xs:all>
   </xs:complexType>
</xs:element>

Diese Deklarationen bewirken, daß die beiden Elemente überall dort verwendet werden können, wo immer ihr Kopf in dem Schema genannt wird, beispielsweise in der Definition der Elemente character und author :

<xs:element name="character">
   <xs:complexType>
      <xs:sequence>
         <xs:element ref="name"/>
         <xs:element ref="born"/>
         <xs:element ref="qualification"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
   </xs:complexType>
</xs:element>
<xs:element name="author">
   <xs:complexType>
      <xs:sequence>
         <xs:element ref="name"/>
         <xs:element ref="born"/>
         <xs:element ref="dead" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute ref="id"/>
   </xs:complexType>
</xs:element>

Abstrakte Elemente

Wenn wir unser Schema so belassen, wie wir es gerade gesehen haben, ist auch die Verwendung des Kopfes in den Instanzdokumenten erlaubt. Da unser Kopfelement beliebige Inhalte zuläßt, ist dies wahrscheinlich nicht das, was wir wollen. Wir benötigen einen Mechanismus ähnlich dem der abstrakten Typen, den wir gesehen haben, als wir unter Erzeugung komplexer Datentypen der gleichen Art von Problemen mit xsi:type begegnet sind. Wir werden das Kopfelement mit Hilfe des Attributs abstract in der Definition des Kopfelements als abstrakt definieren. Damit sieht die Definition so aus:

<xs:element name="name" abstract="true"/>

Bäume von Ersetzungsgruppen

Was, wenn unsere deutsche Niederlassung ein Element composed-name definiert, die dem full-name ähnlich ist, jedoch ohne das Unterelement middle? Wir können dieses Element einfach unmittelbar unserer Ersetzungsgruppe hinzufügen. Wenn wir so jedoch das Element name als Kopf definieren, werden die Ähnlichkeiten zwischen diesem neuen Element und dem Element full-name nicht ersichtlich. Außerdem müssen einige Anwendungen vielleicht angeben, daß sie entweder full-name oder aber composed-name akzeptieren. Die Lösung besteht darin, full-name als Kopf einer neuen Ersetzungsgruppe zu verwenden. Dafür müssen wir den Typ des Elements full-name als global definieren, um die ausdrückliche Ableitung zwischen den beiden Elementen zu zeigen:

<xs:complexType name="full-name-type">
   <xs:all>
      <xs:element name="first" type="string32" minOccurs="0"/>
      <xs:element name="middle" type="string32" minOccurs="0"/>
      <xs:element name="last" type="string32"/>
   </xs:all>
</xs:complexType>
<xs:element name="full-name" substitutionGroup="name" type="full-name-type"/>
<xs:element name="composed-name" substitutionGroup="full-name">
   <xs:complexType>
      <xs:complexContent>
         <xs:restriction base="full-name-type">
            <xs:all>
               <xs:element name="first" type="string32" minOccurs="0"/>
               <xs:element name="last" type="string32"/>
            </xs:all>
         </xs:restriction>
      </xs:complexContent>
   </xs:complexType>
</xs:element>
                
          

Wir haben jetzt nicht nur zwei Ersetzungsgruppen (mit name bzw. full-name als Köpfen) definiert, sondern auch einen Baum von Ersetzungsgruppen, da die erlaubten Ersetzungen für name sowohl full-name als auch simple-name umfassen, zusätzlich jedoch composed-name.

Traditionelle Deklarationen oder Ersetzungsgruppen?

Wenn wir die beiden Lösungen, mit denen wir ein und dasselbe Probelm gelöst haben, noch einmal betrachten, sehen wir, daß Ersetzungsgruppen besser erweiterbar als traditionelle Gruppen mit einem xs:choice-Kompositor sind. Während von der Elementgruppe nur durch Einbindung mit xs:redefine abgeleitet werden kann, kann die Ersetzungsgruppe mit neuen möglichen Elementen erweitert werden, indem man sie einfach definiert. Diese Elemente können in jedem beliebigen Namensraum definiert werden. Die einzige Einschränkung ist, daß ihr Typ der gleiche wie der des Kopfelements oder zumindest eine gültige Ableitung davon sein muß. (Diese Einschränkung ist gerechtfertigt, um sicherzustellen, daß Anwendungen durch unerwartete Inhaltsmodelle nicht allzu stark überrascht werden.)

Man sollte jedoch noch einen Unterschied anmerken. Wir haben gesehen, daß die Ableitung eines Inhaltsmodells mit einem xs:choice-Kompositor den Umfang der Auswahl nicht erweitern kann, indem weitere Alternativen hinzugefügt werden. Die Lage bei Ersetzungsgruppen ist beinahe umgekehrt. Auch wenn die Recommendation besagt, daß Ersetzungsgruppen als Auswahlen validiert werden sollten, definiert sie nicht die Reihenfolge der Elemente in der äquivalenten Auswahl. Ich empfehle, Ersetzungsgruppen in der Praxis nicht einzuschränken, da dies zu Problemen der Interoperabilität zwischen den einzelnen Schema-Prozessoren führen kann.

Wir haben also die paradoxe Situation, daß einer der beiden Mechanismen (xs:choice) nur eingeschränkt werden kann, während der andere (Ersetzungsgruppen) nur erweitert werden kann, und daß die Recommendation dennoch feststellt, daß diese beiden Mechanismen äquivalent sind, was die Validierung angeht. Diese Eigenschaft muß in Betracht gezogen werden, wenn man zwischen den beiden Ansätzen wählt.

Die Unterschiede zwischen den beiden Konstruktionen sind in der folgenden Tabelle zusammengefaßt. »Nicht empfehlenswert« steht für »kann mit einigen Schema-Prozessoren funktionieren, beruht jedoch auf einer freien Interpretation der Recommendation, was zu Interoperabilitätsproblemen führen kann.«

Tabelle: Elementgruppen und Ersetzungsgruppen im Vergleich

KonstruktionElementgruppen mit dem Kompositor xs:choice (außerhalb einer Gruppe)Ersetzungsgruppen
Definitionzentralisiert, verwendet xs:group (Definition) und xs:choice (außerhalb einer Gruppe)über die einzelnen Definitionen globaler Elemente verteilt; verwendet das Attribut substitutionGroup
Beschränkungen für die Auswahlmöglichkeitenkeine Beschränkungen; die Elemente können komplett unterschiedlich seinder Typ der Elemente muß eine explizite Ableitung des Kopftyps sein
Läßt globale Elemente zujaja
Läßt lokale Elemente zujanein
Einschränkung, um Auswahlmöglichkeiten zu entfernenja, mit xs:redefinenicht empfehlenswert
Erweiterung, um Auswahlmöglichkeiten hinzuzufügennicht empfehlenswertja, durch Hinzufügen neuer Elemente mit demselben Kopfelement
Erweiterung, um neue Elemente in der Reihenfolge hinzuzufügenja, mit xs:redefinenein

Unschärfe in der Recommendation

Sowohl die Erweiterung von xs:choice durch Elementgruppen-Redefinitionen als auch die Einschränkung von Ersetzungsgruppen sind in der Recommendation sehr unscharf dargestellt und bedürfen der Erläuterung.

Erweiterung von xs:choice durch Gruppen-Redefinitionen

Wenden wir uns nochmals unserer wie folgt definierten Gruppe zu:

<xs:group name="name">
   <xs:choice>
      <xs:element ref="simple-name"/>
      <xs:element ref="full-name"/>
   </xs:choice>
</xs:group>

Es scheint in der Empfehlung nichts zu geben, was einer Redefinition dieser Gruppe ausdrücklich verbietet, der Auswahl ein anderes Element etwa so hinzuzufügen:

<xs:redefine schemaLocation="foo.xsd">
   <xs:group name="name">
      <xs:choice>
         <xs:group ref="name"/>
         <xs:element ref="bar"/>
      </xs:choice>
   </xs:group>
</xs:redefine>

Das Ergebnis dieser Redefinition wäre jedoch, daß ein neues Element (bar) anstelle von simple-name und full-name akzeptiert würde. Das wäre hübsch. Allerdings sind die Grundsätze einer Redefinition durch Einschränkung (d.h., wenn der Inhalt der Gruppe während einer Redefinition eingeschränkt wird) dieselben wie die der Ableitung komplexer Typen durch Einschränkung. Wir können daraus schließen, daß die Working Group auch die Absicht hat, die Eigenschaften von Redefinitionen durch Erweiterung entsprechend der Ableitung komplexer Typen durch Erweiterung festzulegen, und diese verbietet das Hinzufügen neuer Partikeln bei xs:choice (außerhalb einer Gruppe) ausdrücklich.

Auch wenn einige Schema-Prozessoren diese Konstruktion unterstützen und manche Spezialisten das gut finden, rate ich nicht dazu, es zu verwenden, da es die Absicht (wenn auch nicht den Wortlaut) der Empfehlung zu verletzen scheint.

Einschränkung von Ersetzungsgruppen

Die Einschränkung der Ersetzungsgruppen ist dem ziemlich entgegengesetzt. Die Absicht der Working Group scheint zu sein, solche Einschränkungen zuzulassen, wobei der Wortlaut der Recommendation das Ergebnis undefiniert läßt.

Die Empfehlung legt klar fest, daß Ersetzungsgruppen bei der Prüfung, ob eine Partikel eine gültige Einschränkung einer anderen Partikel ist, wie xs:choice behandelt werden sollten. Dies ist ein klarer Hinweis darauf, daß Ersetzungsgruppen wie bei der Ableitung komplexer Typen durch Einschränkung ebenfalls eingeschränkt werden können. Um dies zu veranschaulichen, nehmen wir die Definition für den komplexen Typ des Elements author, die die Ersetzungsgruppe mit dem Kopf name verwendet, wie wir es oben festgelegt hatten:

<xs:complexType name="authorType">
   <xs:sequence>
      <xs:choice>
         <xs:element ref="name"/>
         <xs:element ref="simple-name"/>
         <xs:element ref="full-name"/>
      </xs:choice>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
   </xs:sequence>
   <xs:attribute ref="id"/>
</xs:complexType>

Wenn Ersetzungsgruppen wie xs:choice behandelt werden, ist diese Definition äquivalent zu der folgenden, vorausgesetzt, der Kopf ist nicht als abstrakt definiert:

<xs:complexType name="authorType">
   <xs:sequence>
      <xs:element ref="name"/>
      <xs:element ref="born"/>
      <xs:element ref="dead" minOccurs="0"/>
   </xs:sequence>
   <xs:attribute ref="id"/>
</xs:complexType>

Es sollte möglich sein, diesen komplexen Typ durch Einschränkung abzuleiten, indem man beispielsweise schreibt:

<xs:complexType name="restrictedAuthorType">
   <xs:complexContent>
      <xs:restriction base="authorType">
         <xs:sequence>
            <xs:choice>
               <xs:element ref="simple-name"/>
               <xs:element ref="full-name"/>
            </xs:choice>
            <xs:element ref="born"/>
            <xs:element ref="dead" minOccurs="0"/>
         </xs:sequence>
      </xs:restriction>
   </xs:complexContent>
</xs:complexType>

Die Recommendation besagt jedoch auch, daß es während der Ableitung durch Einschränkung bei einem xs:choice-Kompositor »eine vollständige, die Reihenfolge bewahrende funktionale Abbildung« zwischen den Partikeln gibt, die für die Definition der abgeleiteten und der ursprünglichen xs:choice verwendet werden. Sie legt allerdings die Reihenfolge der Partikeln nicht fest, wenn Ersetzungsgruppen auf xs:choice abgebildet werden. Je nach der vom Schema-Validierer gewählten Reihenfolge bei der Konstruktion der xs:choice aus der Ersetzungsgruppe kann unsere Ableitung daher entweder gültig oder aber ungültig sein.

   

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