XML – ćwiczenia 3: XML Schema – typy złożone

XML Schema

Standard XML Schema zdefiniowany jest w trzech rekomendacjach:

XML Schema Part 0: Primer,
wprowadzenie i mniej formalny opis standardu,
XML Schema Part 1: Structures
definiowanie typów złożonych (formalnie),
XML Schema Part 2: Datatypes
definiowanie typów prostych (formalnie).

Istnieje też tutorial W3 Schools po XML Schema...

Ogólnie

XML Schema sam jest zastosowaniem XML, to znaczy schematy są dokumentami XML. Elementy XML Schema znajdują się w przestrzeni nazw http://www.w3.org/2001/XMLSchema, w schematach zwykło się używać dla niej prefiksów xs lub xsd. Standardowym rozszerzeniem nazwy dla pliku ze schematem jest xsd.

W schemacie na najwyższym poziomie, tzn. jako dzieci elementu głównego schema, występują definicje:

  • elementów i atrybutów,
  • typów (prostych i złożonych),
  • grup (elementów i atrybutów),
  • notacji.

Ponadto na początku dokumentu mogą wystąpić elementy związane z łączeniem wielu schematów, a w dowolnym miejscu anotacje, m.in. dokumentacja schematu (opisy elementów, atrybutów, typów itd.).

Typy nazwane

W schematach elementom i atrybutom o różnych nazwach przypisuje się typ zawartości. Można to zrobić w poniższy sposób, odwołując się do typu nazwanego.

Przykład 1.

Plik: osoby1.xsd.

<xs:element name="osoba" type="OsobaTyp"/>
<xs:element name="imie" type="xs:string"/>
<xs:element name="nazwisko" type="xs:string"/>
<xs:attribute name="plec" type="PlecTyp"/>
<xs:attribute name="email" type="xs:string"/>

W XML Schema istnieją typy predefiniowane, m.in. występujący w przykładzie string. Nazwy tych typów znajdują się w przestrzeni nazw XML Schema i dlatego nazwa jest poprzedzona prefiksem.

Własne typy nazwane można zdefiniować w poniższy sposób, nieco odmienny dla typów prostych i złożonych.

Przykład 2.

Plik: osoby1.xsd.

<xs:complexType name="OsobaTyp">
	<xs:sequence>
		<xs:element ref="imie" minOccurs="1" maxOccurs="unbounded"/>
		<xs:element ref="nazwisko"/>
	</xs:sequence>
	<xs:attribute ref="plec" use="required"/>
	<xs:attribute ref="email" use="optional"/>
</xs:complexType>

<xs:simpleType name="PlecTyp">
	<xs:restriction base="xs:string">
		<xs:enumeration value="k"/>
		<xs:enumeration value="m"/>
	</xs:restriction>
</xs:simpleType>

Typów nazwanych należy używać wtedy, gdy wiele elementów / atrybutów posiada ten sam typ zawartości. Tylko typy nazwane mogą stanowić podstawę do rozszerzania bądź zawężania (więcej na przyszłych zajęciach).

Typy anonimowe

Definicję typu można umieścić też wewnątrz definicji elementu (lub atrybutu). Wówczas mamy do czynienia z typem anonimowym.

Przykład 3.

Plik: osoby2.xsd.

<xs:element name="osoba">
	<xs:complexType>
		<xs:sequence>
			<xs:element ref="imie" minOccurs="1" maxOccurs="unbounded"/>
			<xs:element ref="nazwisko"/>
		</xs:sequence>
		<xs:attribute ref="plec" use="required"/>
		<xs:attribute ref="email" use="optional"/>
	</xs:complexType>
</xs:element>

<xs:attribute name="plec">
	<xs:simpleType>
		<xs:restriction base="xs:string">
			<xs:enumeration value="k"/>
			<xs:enumeration value="m"/>
		</xs:restriction>
	</xs:simpleType>
</xs:attribute>

Typów anonimowych należy używać tylko wtedy, gdy tylko jeden element / atrybut posiada dany typ zawartości i nie przewidujemy dodania do schematu nowych elementów / atrybutów z takim typem.

Typy złożone w XML Schema

Typy złożone definiuje się elementem schematu complexType. Typ złożony, w odróżnieniu od typu prostego, może posiadać wewnętrzną strukturę: podelementy i atrybuty. Dlatego tylko elementom (a nie atrybutom) można przypisywać typy złożone.

Podelementy i atrybuty

W definicji typu złożonego T określa się jakie podelementy i atrybuty może zawierać element o typie T. Ponadto dla podelementów określa się ich kolejność i dopuszczalne krotności, a dla atrybutów ich opcjonalność oraz wartości domyślne.

Przykład 4.

Na przykład elementy o poniższym typie mogą zawierać dowolną niezerową liczbę elementów imie, a następnie dokładnie jeden element nazwisko. Poza tym element musi mieć atrybut plec i może mieć atrybut email. Atrybuty i elementy są zdefiniowane gdzieś indziej w schemacie (stąd atrybut ref).

<xs:complexType name="OsobaTyp">
	<xs:sequence>
		<xs:element ref="imie" minOccurs="1" maxOccurs="unbounded"/>
		<xs:element ref="nazwisko" minOccurs="1" maxOccurs="1"/>
	</xs:sequence>
	<xs:attribute ref="plec" use="required"/>
	<xs:attribute ref="email" use="optional"/>
</xs:complexType>

Domyślną liczbą wystąpień podelementu jest dokładnie 1, a atrybuty domyślnie są opcjonalne.

Sekwencja, wybór i wszystko

Element schematu sequence, użyty w poprzednim przykładzie, grupuje podelementy w sekwencję. Istnieją trzy elementy schematu służące do konstruowania takich grup elementów:

  • sequence – w dokumencie muszą występować wszystkie podane podelementy w tej samej kolejności,
  • choice – w dokumencie musi wystąpić dokładnie jeden z wymienionych podelementów (w takiej liczbie, jaka jest zgodna z jego minOccurs i maxOccurs),
  • all – w dokumencie muszą występować wszystkie podane podelementy, ale w dowolnej kolejności; w tym przypadku dla podelementów musi zachodzić maxOccurs <= 1.

Sekwencje i wybory można w sobie zagnieżdżać, konstruując bardziej skomplikowane typy zawartości. W zagnieżdżeniach nie mogą brać udziału grupy all.

Przykład 5.

Plik: przyklad1.xsd.

<xs:complexType name="typ1">
		<!-- Dokładnie jeden element A lub od 2 do 3 elementów B. -->
	<xs:choice>
		<xs:element ref="A"/>
		<xs:element ref="B" minOccurs="2" maxOccurs="3"/>
	</xs:choice>
</xs:complexType>
	
<xs:complexType name="typ2">
		<!-- Dokładnie jeden element Start, następnie dowolna liczba wystąpień grup,
			z których każda jest parą elementów A albo parą elementów B i C,
			na końcu dokładnie jeden element Koniec. -->
	<xs:sequence>
		<xs:element ref="Start"/>
		<xs:choice minOccurs="0" maxOccurs="unbounded">
			<xs:element ref="A" minOccurs="2" maxOccurs="2"/>
			<xs:sequence>
				<xs:element ref="B"/>
				<xs:element ref="C"/>
			</xs:sequence>
		</xs:choice>
		<xs:element ref="Koniec"/>
	</xs:sequence>
</xs:complexType>

Zadanie 1.

Otwórz w widoku tekstowym XML Spy ten schemat. Spróuj zdefiniować następujące typy zawartości:

  1. od 2 do 3 elementów A, następnie dokładnie jeden element B i opcjonalny element C.
  2. dowolnie wiele razy powtórzony wybór między pojedynczym elementem A, dwoma elementami B lub trzeba elementami C,
  3. parzysta lub podzielna przez 3 liczba elementów A,
  4. elementy A, B i opcjonalny element C, w dowolnej kolejności.
  5. od 2 do 3 elementów A, jeden element B i opcjonalny element C, wszystko w dowolnej kolejności.

Wyjaśnienie: XML Schema wymaga aby typy zawartości w schemacie były deterministyczne. W grupach typu all elementy mogą występować najwyżej jeden raz.

Zadanie 2.

Napisz w widoku tekstowym XML Spy schemat dla wizytówek. Wszędzie dla zawartości tekstowej zastosuj predefiniowany typ string.

Wizytówka powinna zawierać imię, nazwisko, telefon, email, adres pocztowy. Zastanów się nad liczbą i kolejnością poszczególnych elementów, opcjonalnością atrybutów.

Zadanie 3.

Napisz wizytówkę zgodną ze swoim schematem (lub wykorzystaj wizytówkę z poprzednich zajęć) i zwaliduj ją.

Deterministyczny model zawartości – wyjaśnienie

Model zawartości zdefiniowany w XML Schema musi być deterministyczny. Problem ten jest opisany w rekomendacjach w następujących miejscach:

Intuicyjnie determinizm polega na tym, że dla każdego dokumentu podczas czytania dokumentu po napotkaniu początku kolejnego elementu parser jest w stanie jednoznacznie wskazać pozycję w schemacie (lub DTD), która temu elementowi odpowiada, bez analizowania kolejnych elementów.

Wiele modeli zapisanych niedeterministycznie daje się zapisać deterministycznie. Na przykład (notacja z DTD):

  • (a,b)|(a,c) można zapisać jako a,(b|c) ,

Istnieją jednak modele zawartości, których nie da się wyrazić deterministycznie, np.:

  • (a,b)*,a? ,
  • (aa)*|(aaa)* z zadania.

Grupy elementów

Gdy jakaś grupa (sekwencja, wybór lub all) powtarza się w wielu miejscach (np. w różnych definicjach typu), można w jednym miejscu zdefiniować nazwaną grupę elementów, a w definicjach typów odwoływać się do tej grupy.

Przykład 6.

W schemacie figury1.xsd występują powtarzające się sekwencje elementów x i y. W schemacie figury2.xsd zostały zastąpione grupą współrzędne, jak poniżej.

<xs:group name="współrzędne">
	<xs:sequence>
		<xs:element name="x" type="xs:double"/>
		<xs:element name="y" type="xs:double"/>
	</xs:sequence>
</xs:group>

<xs:element name="odcinek">
	<xs:complexType>
		<xs:sequence>
			<xs:group ref="współrzędne" minOccurs="2" maxOccurs="2"/>
		</xs:sequence>
		<xs:attributeGroup ref="atrybuty-krawędzi"/>
	</xs:complexType>
</xs:element>

Grupy atrybutów

Podobnie jak grupy elementów, można także zdefiniować grupy atrybutów i odwoływać się do nich w definicjach typów.

Przykład 7.

W schemacie figury1.xsd występują powtarzające się atrybuty kolor-krawędzi, grubość-krawędzi i kolor-powierzchni. W schemacie figury2.xsd zostały zastąpione grupami atrybutów atrybuty-krawędzi i atrybuty-powierzchni, jak poniżej.

<xs:attributeGroup name="atrybuty-krawędzi">
	<xs:attribute name="kolor-krawędzi" type="KolorTyp"/>
	<xs:attribute name="grubość-krawędzi" type="xs:positiveInteger"/>
</xs:attributeGroup>

<xs:element name="punkt">
	<xs:complexType>
		<xs:sequence>
			<xs:group ref="współrzędne" minOccurs="1" maxOccurs="1"/>
		</xs:sequence>
		<xs:attributeGroup ref="atrybuty-krawędzi"/>
	</xs:complexType>
</xs:element>

Mieszany typ zawartości

W XML węzły tekstowe mogą występować obok węzłów elementów jako dzieci tego samego elementu. W tekstowej reprezentacji XML można to opisać jako tekst z zanurzonymi elementami albo znakowany tekst.

Typ zawartości, który dopuszcza tekst z zanurzonymi elementami, nazywa się typem mieszanym (mixed content type) i można go zdefiniować w XML Schema. Służy do tego atrybut mixed w elemencie complexType.

Włączenie typu mieszanego pozwala na pojawienie się węzłów tekstowych pomiędzy podelementami. Ważne jest to, że liczba i kolejność podelementów nadal jest kontrolowana zgodnie z definicją typu (inaczej niż w DTD).

Przykład 8.

Poniższe przykłady pochodzą z rekomendacji XML Schema.

<xsd:element name="letterBody">
  <xsd:complexType mixed="true">
    <xsd:sequence>
      <xsd:element name="salutation">
        <xsd:complexType mixed="true">
          <xsd:sequence>
            <xsd:element name="name" type="xsd:string"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="quantity"    type="xsd:positiveInteger"/>
      <xsd:element name="productName" type="xsd:string"/>
      <xsd:element name="shipDate"    type="xsd:date" minOccurs="0"/>
      <!-- etc. -->
    </xsd:sequence>
  </xsd:complexType>
</xsd:element>
<letterBody>
<salutation>Dear Mr.<name>Robert Smith</name>.</salutation>
Your order of <quantity>1</quantity> <productName>Baby
Monitor</productName> shipped from our warehouse on
<shipDate>1999-05-21</shipDate>. ....
</letterBody>

Zadanie 4.

Zapisz schemat opisujący znane z HTML elementy dla list ol, ul, li oraz kilka przykładowych elementów do znakowania tekstu. Użyj typów nazwanych i/lub grup elementów aby nie definiować identycznych typów zawartości wielokrotnie.

Niech element body zawiera tekst z zanurzonymi w nim listami, listy tylko elementy wypunktowania (li), a elementy wypunktowania znowu tekst z zanurzonymi listami. Ponadto w każdym miejscu gdzie dopuszczalny jest tekst, może być on znakowany elementami b oraz i i mogą w nim występować puste elementy br.

Element ol niech posiada atrybut firstNumber, którego zawartością mogą być liczby całkowite, z wartościa domyślną 1.

Globalne i lokalne definicje

Definicje globalne

Elementy i atrybuty można definiować na głównym poziomie schematu i odwoływać się do nich w definicjach typów w atrybucie ref.

Takie podejście jest zalecane dla elementów i atrybutów, które występują w wielu typach, zawsze z tym samym typem zawartości. Tylko element zdefiniowany globalnie (i każdy taki element) może być elementem głównym dokumentu.

Przykładami schematów napisanych w ten sposób są osoby1.xsd, osoby2.xsd i przyklad2.xsd.

Definicje lokalne

Elementy i atrybuty można także definiować lokalnie, wewnątrz definicji typu (nazwanego lub anonimowego). Definicje można zagnieżdżać. Nazwę podaje się wówczas w atrybucie name.

Takie podejście jest zalecane dla elementów i atrybutów, które występują tylko w jednym miejscu schematu lub w różnych miejscach schematu mają różny typ zawartości (patrz poniższy podrozdział). Definiując element lokalnie można także wymusić, aby nie był elementem głównym dokumentu.

Przykładami schematów napisanych w ten sposób są osoby3.xsd, osoby4.xsd i przyklad3.xsd.

Schemat jest bardziej czytelny, gdy definicje nie są zbytnio zagnieżdżone (chociaż to może kwestia gustu). Dlatego lepiej, aby lokalne definicje elementów / atrybutów (w jakimś typie) same nie zawierały już skomplikowanego typu anonimowego, a najlepiej odwoływały się do typu nazwanego.

Różne typy zawartości dla elementów (atrybutów) o tych samych nazwach

Lokalna definicja elementu / atrybutu obowiązuje tylko w miejscu jej wystąpienia. W innym miejscu schematu można zdefiniować element / atrybut o tej samej nazwie, ale o innym typie zawartości.

Na przykład w schemacie przyklad2.xsd element c zdefiniowany jest globalnie, w każdym miejscu posiada ten sam typ zawartości: liczby całkowite dodatnie. Dokument przyklad2.xml jest zgodny z tym schematem.

Natomiast w schemacie przyklad3.xsd element c zdefiniowany jest lokalnie dwa razy. c położone wewnątrz elementów a mogą zawierać liczby dodatnie, natomiast c położone wewnątrz elementów b mogą zawierać liczby ujemne. Dokument przyklad2.xml nie jest zgodny z tym schematem, natomiast zgodny jest dokument przyklad3.xml.

Stosowanie lokalnych definicji z (potencjalnie) różnym typem zawartości może być zaletą. Dzięki temu możemy stosować te same nazwy, jeśli są naturalne w danym miejscu. Element kod wewnątrz artykułu może oznaczać kod źródłowy, a wewnątrz adresu kod pocztowy. Dzięki XML Schema możemy dla takich elementów podać różne typy zawartości (nie użwając różnych przestrzeni nazw przestrzeni nazw).

Jednak nadawanie różnego znaczenia i różnego typu zawartości elementom o tej samej nazwie może prowadzić do niejednoznaczności i utrudniać rozumienie schematu oraz odczytywanie informacji z dokumentu. Dlatego tej techniki można używać tylko w naprawdę uzasadnionych sytuacjach.

W pełni uzasadnione jest lokalne definiowanie atrybutów, gdyż ich znaczenie (i typ zawartości) są ściśle związane z elementem, w którym występują. Jednak w przypadku atrybutów, które występują w wielu elementach (typach zawartości) w tym samym znaczeniu, zalecane jest ich globalne definiowanie.


Valid XHTML 1.1Valid CSS