Pro rychlé vpravení do problematiky si rovnou ukážeme jednoduchý příklad XML schématu a dokumentu XML, který mu vyhovuje. Dejme tomu, že chceme vytvořit schéma popisující dokumenty XML vhodné pro přenos informace o jednom zaměstnanci. U zaměstnance nás přitom bude zajímat jeho identifikační číslo, jméno, příjmení, plat a datum narození. Tyto informace můžeme v XML zachytit následujícím způsobem:
<zamestnanec id="101"> <jmeno>Jan</jmeno> <prijmeni>Novák</prijmeni> <plat>25000</plat> <narozen>1965-12-24</narozen> </zamestnanec>
Odpovídající schéma tedy musí definovat, že dokument musí
obsahovat element zamestnanec, který obsahuje
atribut id a čtyři podelementy
jmeno, prijmeni,
plat a narozen. Obsah těchto
elementů přitom musí odpovídat určitému datovému typu. Všechny tyto
požadavky může zachytit následující jednoduché XML schéma.
w
x
s<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="zamestnanec">
<xs:complexType>
<xs:sequence>
<xs:element name="jmeno" type="xs:string"/>
<xs:element name="prijmeni" type="xs:string"/>
<xs:element name="plat" type="xs:decimal"/>
<xs:element name="narozen" type="xs:date"/>
</xs:sequence>
<xs:attribute name="id" type="xs:integer"/>
</xs:complexType>
</xs:element>
</xs:schema>Vidíme, že celé schéma je dokument XML, který používá speciální
elementy. Všechny tyto elementy musí patřit do jmenného prostoru
http://www.w3.org/2001/XMLSchema. Obvykle se pro
tento jmenný prostor používá prefix xs nebo
xsd.
Celé schéma musí být vždy uzavřeno v elementu schema obsahujícím definice elementů, které lze
použít jako kořenové elementy, tj. dokument XML jimi začíná. Definice
elementu se zapisuje pomocí elementu
element.
Pro každý element musí schéma určit jeho typ. Rozlišovány jsou
přitom dva druhy typů – jednoduché a komplexní. Jednoduché typy
se používají pro skalární hodnoty jako řetězec, číslo, datum apod.
Obsahuje-li element však další elementy nebo atributy, musíme použít
komplexní typ (complexType). Pomocí elementu
sequence říkáme, že zaměstnanec se skládá z po
sobě následujících elementů pro jméno, příjmení, plat a datum
narození. Každý z těchto elementů je povinný, má určené své jméno
pomocí atributu name a datový typ
pomocí atributu type. Datové typy
se uvádějí jako kvalifikované názvy patřící rovněž do jmenného
prostoru XML schémat.
Atributy se deklarují až za vnořenými elementy pomocí elementu
attribute. U atributu rovněž musíme určit jeho
název a datový typ.
Datové typy jsou samotným základem XML schémat, protože v nich je v podstatě vše datový typ. Jak jsme již řekli, datové typy mohou být jednoduché či komplexní.
Největší síla XML schémat spočívá v možnosti definovat si vlastní datové typy. Ty přitom mohou vznikat zejména restrikcí nebo rozšíření některého z již existujících typů. Jako základ pro odvozování našich uživatelských typů můžeme použít některý ze zabudovaných typů. Na obrázku 3.1 – „Hierarchie zabudovaných datových typů“ si můžete prohlédnout hierarchii zabudovaných datových typů. Obrázek byl převzat přímo ze specifikace [8].
XML schémata obsahují běžné základní datové typy jako textový
řetězec, celá a desetinná čísla, binární data, logická hodnota, datum,
čas, časový interval a několik typů převzatých z DTD pro jejich
snazší konverzi do XML schémat. V mnoha případech si
s těmito typy vystačíme. Naše první ukázka si například naprosto
vystačila se zabudovanými typy pro textové řetězce
(string), datum (date) a
desetinné číslo (decimal).
Tabulka 3.1. Zabudované datové typy
| Typ | Popis | Ukázka/Poznámka |
|---|---|---|
| string | řetězec znaků | ahoj |
| boolean | logická hodnota | true, false,
1, 0 |
| decimal | desetinné číslo |
desetinné číslo s přesností nejméně 18 platných číslic |
| float | desetinné číslo |
32bitové číslo v plovoucí desetinné čárce |
| double | desetinné číslo |
64bitové číslo v plovoucí desetinné čárce |
| duration | délka časového intervalu |
ukázka reprezentuje interval o délce jeden rok, dva měsíce, tři dny, deset hodin a třicet minut |
| dateTime | datum a čas |
údaj je ve formátu ISO8601 a může obsahovat i časovou zónu |
| time | čas |
časový údaj ve formátu ISO8601; může obsahovat časovou zónu |
| date | datum |
datum ve formátu ISO8601, tj. rok-měsíc-den |
| gYearMonth | měsíc v daném roce |
ukázka reprezentuje září 1975 |
| gYear | rok | 2005 |
| gMonthDay | den v daném měsíci | --12-24 hodí se pro zachycení datumu, který se každý rok opakuje; ukázka např. reprezentuje Štědrý večer |
| gDay | určitý den v měsíci |
hodí se pro zachycení dne, ve kterém se každý měsíc něco opakuje; ukázka může například reprezentovat, že výplata je vždy dvanáctého |
| gMonth | určitý měsíc v roce |
ukázka reprezentuje měsíc květen |
| hexBinary | binární data |
binární data jsou zakódována tak, že každému bajtu odpovídají dva znaky reprezentující hodnotu bajtu v šestnáctkové soustavě |
| base64Binary | binární data |
binární data jsou zakódována metodou Base64 |
| anyURI | adresa URI |
jakákoliv URI adresa; může být absolutní i relativní |
| NOTATION | notace externích dat | v praxi téměř nepoužívaný typ, který byl do WXS převzat z DTD |
| normalizedString | normalizovaný řetězec znaků |
před kontrolou dalších integritních omezení jsou v řetězci konce řádků a tabulátory nahrazeny mezerou |
| token | ještě více normalizovaný řetězec znaků |
před kontrolou dalších integritních omezení jsou v řetězci konce řádků a tabulátory nahrazeny mezerou, opakující se mezery nahrazeny mezerou jedinou a jsou odstraněny mezery na začátku a konci řetězce |
| language | jazykový kód |
kód identifikující jazyk |
| NMTOKEN | typ byl převzat z DTD kvůli zpětné kompatibilitě; lze jej používat pouze pro atributy | |
| NMTOKENs | typ byl převzat z DTD kvůli zpětné kompatibilitě; lze jej používat pouze pro atributy | |
| Name | jméno |
jméno musí splňovat pravidla XML pro názvy elementů/atributů |
| NCName | jméno bez dvojtečky |
podobné jako typ |
| ID | unikátní identifikátor |
převzatý z DTD kvůli zpětné kompatibilitě |
| IDREF | odkaz na identifikátor |
převzatý z DTD kvůli zpětné kompatibilitě |
| IDREFS | odkazy na několik identifikátorů |
převzatý z DTD kvůli zpětné kompatibilitě |
| ENTITY | odkaz na externí binární entitu | převzatý z DTD kvůli zpětné kompatibilitě; může se použít pouze pro definici atributu |
| ENTITIES | odkazy na externí binární entity | převzatý z DTD kvůli zpětné kompatibilitě; může se použít pouze pro definici atributu |
| integer | celé číslo | -17, 0,
65542 |
| nonPositiveInteger | nekladné celé číslo |
celé číslo menší nebo rovné 0 |
| negativeInteger | záporné celé číslo |
celé číslo menší než 0 |
| long | celé číslo |
číslo je v rozsahu od -9223372036854775808 do 9223372036854775807, což odpovídá 64bitovému celému číslu |
| unsignedLong | nezáporné celé číslo |
číslo je v rozsahu od 0 do 18446744073709551615, což odpovídá 64bitovému celému číslu |
| int | celé číslo |
číslo je v rozsahu od -2147483648 do 2147483647, což odpovídá 32bitovému celému číslu |
| unsignedInt | nezáporné celé číslo |
číslo je v rozsahu od 0 do 4294967295, což odpovídá 32bitovému celému číslu |
| short | celé číslo |
číslo je v rozsahu od -32768 do 32767, což odpovídá 16bitovému celému číslu |
| unsignedShort | nezáporné celé číslo |
číslo je v rozsahu od 0 do 65535, což odpovídá 16bitovému celému číslu |
| byte | celé číslo |
číslo je v rozsahu od -128 do 127, což odpovídá 8bitovému celému číslu |
| unsignedByte | nezáporné celé číslo |
číslo je v rozsahu od 0 do 255, což odpovídá 8bitovému celému číslu |
| nonNegativeInteger | nezáporné celé číslo |
celé číslo větší nebo rovné 0 |
| positiveInteger | kladné celé číslo |
celé číslo větší než 0 |
Vyžadujeme-li však v aplikacích striktnější kontrolu dat, musíme si od zabudovaných typů odvodit vlastní typy. Nejběžnějším způsobem odvození je restrikce, kdy pomocí integritních omezení zúžíme obor přípustných hodnot. Kromě toho lze odvozovat nové typy vytvořením seznamu a sjednocením typů.
Nejpoužívanějším typem odvození nového typu je bezesporu restrikce. Na existující datový typ můžeme najednou aplikovat několik integritních omezení a tak zúžit přípustné hodnoty.
Podívejme se nejprve na integritní omezení použitelná pro
textové řetězce. Nejjednodušším z nich je
length, které umožňuje definovat přesnou délku
řetězce. Nový datový typ pro PSČ tak můžeme snadno odvodit ze
zabudovaného typu pro řetězce:
w
x
s<xs:simpleType name="PSČType">
<xs:restriction base="xs:string">
<xs:length value="6"/>
</xs:restriction>
</xs:simpleType>Element simpleType slouží
k definici nového jednoduchého datového typu. Atribut name pak nese jméno nového
typu. Element restriction říká, že
odvození probíhá právě restrikcí od typu uvedeného v atributu
base (v našem
případě odvozujeme od běžného textového řetězce). Uvnitř elementu
restriction pak uvádíme všechna omezení,
v našem případě jen omezení na přesnou délku řetězce.
Nově definovaný typ můžeme použít při definici elementů a atributů nebo při definici dalších z něj odvozených typů. Nově definované typy se při vytváření elementů používají stejně jako zabudované typy:
w
x
s<xs:element name="psc" type="PSČType"/>Mezi další omezení aplikovatelná na řetězcové typy patří
minimální (minLength) a maximální délka
(maxLength).
w
x
s<xs:simpleType name="jménoType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="15"/>
</xs:restriction>
</xs:simpleType>Můžeme rovněž určit výčet přípustných hodnot pro datový typ:
w
x
s<xs:simpleType name="kódMěnyType">
<xs:restriction base="xs:string">
<xs:enumeration value="CZK"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="USD"/>
</xs:restriction>
</xs:simpleType>Velmi silným nástrojem jsou regulární výrazy, které umožňují
zapsání masky, jíž musí hodnota vyhovět. Pro zápis omezení na základě
regulárního výrazu se používá element pattern. XML schémata používají stejnou syntaxi
regulárních výrazů jako jazyk Perl.
w
x
s<xs:simpleType name="PSČType">
<xs:restriction base="xs:string">
<xs:pattern value="\d{3} \d{2}"/>
</xs:restriction>
</xs:simpleType>U číselných typů můžeme omezit přípustné hodnoty zdola
(minInclusive, minExclusive)
i shora (maxInclusive,
maxExclusive). Navíc lze definovat celkový počet
platných číslic (totalDigits) a počet míst za
desetinnou čárkou (fractionDigits).
w
x
s<xs:simpleType name="částkaType">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0"/>
<xs:maxExclusive value="1000000"/>
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>Chceme-li pro nějaký element nebo atribut použít vlastní datový typ, nemusíme jej ve schématu přímo deklarovat. Lze používat i anonymní datové typy, které definují uživatelský typ pouze pro jeden element či atribut a nejde se na ně odkazovat z jiných definic. Příklad s definicí elementu pro PSČ tak můžeme alternativně zapsat jako:
w
x
s<xs:element name="psc">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="6"/>
</xs:restriction>
</xs:simpleType>
</xs:element>Pro správné pochopení integritních omezení je potřeba si uvědomit, že hodnota, kterou kontrolují, nemusí být stejná jako hodnota zapsaná přímo do dokumentu XML.
Jak ukazuje obrázek 3.2 – „Jak se text z dokumentu XML přemění na hodnotu“ údaj
uložený v dokumentu XML se nejprve normalizuje. Při této normalizaci
se bílé znaky jako konce řádky a tabulátory nahradí
mezerou. Poté se oříznou veškeré mezery na začátku a konci hodnoty
a vícenásobný výskyt znaku mezera se nahradí mezerou jedinou. Tato
normalizace se provádí vždy s výjimkou typů string,
normalizedString a od nich odvozených uživatelských
typů. Pro typ string se neprovádí vůbec žádná
normalizace a pro normalizedString se pouze znaky
konce řádků a tebulátory převedou na mezery.
Po normalizaci údaje z dokumentu XML získáme jeho lexikální
reprezentaci. Nad tímto lexikálním prostorem pracuje integritní
omezení pomocí regulárního výrazu (pattern). Všechny ostatní kontroly, jako počet
desetinných míst, přípustný rozsah hodnot apod., se provádějí
až nad prostorem hodnot. Ten obsahuje již abstraktní reprezentaci
původního údaje. Na obrázku 3.2 – „Jak se text z dokumentu XML přemění na hodnotu“ je
tak vidět, jak se původně tři různé zápisy nakonec převedly na stejnou
hodnotu 3½.
Komplexní typy slouží k modelování struktury dokumentu, protože se mohou skládat z elementů a atributů. U elementů můžeme určit v jakém pořadí se mají vyskytovat, kolikrát se mohou opakovat, zda jsou povinné či volitelné.
Komplexní typ se definuje pomocí elementu
complexType. Podobně jako u jednoduchých typů
můžeme definovat komplexní typ samostatně, aby pak šel používat
opakovaně pro různé elementy. Následující příklad ukazuje, jak
definovat elementy odberatel a
dodavatel tak, že mají stejný obsah, právě pomocí
společně použitého uživatelsky definovaného komplexního typu
subjektType.
w
x
s<xs:element name="faktura">
<xs:complexType>
<xs:sequence>
<xs:element name="odberatel" type="subjektType"/>
<xs:element name="dodavatel" type="subjektType"/>
...
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="subjektType">
<xs:sequence>
<xs:element name="nazev" type="xs:string" />
<xs:element name="adresa" type="xs:string" />
<xs:element name="ico" type="xs:string" />
<xs:element name="dic" type="xs:string" />
</xs:sequence>
</xs:complexType>Druhou možností je použít complexType přímo
v definici elementu. Takto je v předchozím příkladě
definován obsah elementu faktura.
Uvnitř elementu complexType pak
můžeme použít další elementy sequence,
choice a all. Elementy definované uvnitř sequence se musí v dokumentu vyskytovat
v definovaném pořadí. Oproti tomu all říká, že elementy se mohou vyskytovat
v libovolném pořadí. Konečně choice
říká, že se v dokumentu může vyskytnout jen jeden
z obsažených elementů. Tyto elementy lze do sebe podle potřeby
zanořovat (s výjimkou all) a tak
modelovat složitější situace.
Následující příklad definuje schéma pro článek, který má název, libovolný počet autorů a pak se v něm podle potřeby vyskytuje libovolný počet obrázků anebo odstavců.
w
x
s<xs:element name="clanek">
<xs:complexType>
<xs:sequence>
<xs:element name="nazev" type="xs:string"/>
<xs:element name="autor" type="xs:string"
minOccurs="0" maxOccurs="unbounded"/>
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="odstavec" type="xs:string"/>
<xs:element name="obrazek" type="xs:base64Binary"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>Příklad také demonstruje, jakým způsobem lze určovat počet
výskytů elementu nebo celé jejich skupiny. Slouží k tomu atributy
minOccurs a maxOccurs. Jejich standardní
hodnota je jedna. Pro nekonečno se používá identifikátor
unbounded.
Nejpoužívanější konstrukcí uvnitř komplexního typu je sekvence
elementů (sequence). Tento element říká, že
v něm obsažené definice elementů (případně dalších vnořených
struktur) se musí v dokumentu vyskytovat přesně v tom
pořadí, jak jsou uvedeny. Počet výskytů jednotlivých elementů je možné
ovlivnit právě pomocí atributů minOccurs a maxOccurs. Implicitně mají oba hodnotu
jedna, což odpovídá povinnému výskytu elementu.
Následující příklad definuje element pro uložení článku, který má povinný název, nepovinného autora a dále následuje libovolný počet odstavců, přičemž vždy musí být uveden alespoň jeden odstavec.
Příklad 3.1. Sekvence elementů
<článek> <název>Ukázka</název> <autor>Pepa</autor> <odstavec>...</odstavec> <odstavec>...</odstavec> </článek>
w
x
s<xs:element name="článek">
<xs:complexType>
<xs:sequence>
<xs:element name="nadpis" type="xs:string"/>
<xs:element name="autor" type="xs:string" minOccurs="0"/>
<xs:element name="odstavec" type="xs:string" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>Chceme-li říci, že na určitém místě dokumentu se může vyskytovat
jeden z několika elementů, použijeme k tomu konstrukci
choice. Na jejím místě se pak může vyskytnout
jakýkoliv element definovaný uvnitř této konstrukce.
Následující příklad ukazuje schéma, které modeluje seznam osob. U každé osoby přitom musí být uveden jeden ze tří identifikátorů – rodné číslo, číslo pasu nebo číslo sociálního pojištění.
Příklad 3.2. Výběr jednoho z elementů
<osoby>
<osoba>
<jméno>Pepa Tuzemec</jméno>
<RČ>681203/0123</RČ>
</osoba>
<osoba>
<jméno>Pepa Cizinec</jméno>
<pas>1234567</pas>
</osoba>
<osoba>
<jméno>Pepa Rozvědčík</jméno>
<SSN>987654321</SSN>
</osoba>
</osoby>w
x
s<xs:element name="osoby">
<xs:complexType>
<xs:sequence>
<xs:element name="osoba" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="jméno" type="xs:string"/>
<xs:choice>
<xs:element name="RČ" type="xs:string"/>
<xs:element name="pas" type="xs:string"/>
<xs:element name="SSN" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>V mnoha případech nám nezáleží na pořadí, v jakém jsou
nějaké elementy uvedeny a nechceme uživatele zbytečně nutit
k tomu, aby dodržoval nějaké konkrétní pořadí. V tomto
případě se obsah komplexního typu definuje pomocí elementu
all. Nepříjemným omezením je, že
u jednotlivých elementů ve skupině all můžeme
počet jejich výskytů definovat pouze v rozmezí od nuly do jedné.
Větší počet výskytů není dovolen, protože by se příliš zesložitil
model obsahu.
Následující příklad ukazuje definici elementu
osoba, který může obsahovat jméno a příjmení
v libovolném pořadí, navíc se v elementu může vyskytovat
i titul.
Příklad 3.3. Elementy v libovolném pořadí
<osoba> <jméno>Jan</jméno> <příjmení>Novák</příjmení> </osoba> <osoba> <příjmení>Novák</příjmení> <jméno>Jan</jméno> </osoba> <osoba> <titul>Ing.</titul> <jméno>Jan</jméno> <příjmení>Novák</příjmení> </osoba> <osoba> <jméno>Jan</jméno> <příjmení>Novák</příjmení> <titul>CSc.</titul> </osoba>
w
x
s<xs:element name="osoba">
<xs:complexType>
<xs:all>
<xs:element name="jméno" type="xs:string"/>
<xs:element name="příjmení" type="xs:string"/>
<xs:element name="titul" type="xs:string" minOccurs="0"/>
</xs:all>
</xs:complexType>
</xs:element>Uvedené schéma však není schopno zachytit případy, kdy má jeden
člověk tituly dva, jeden před jménem a druhý za jménem, protože uvnitř
all nejde elementům nastavit maxOccurs na větší hodnotu než jedna.
Vzhledem k tomu, že možnost kombinování all
s ostatními konstrukty je velmi omezená,[2] je potřeba se při řešení našeho problému obejít bez
all a ručně vypsat všechny kombinace elementů
v různém pořadí.
Příklad 3.4. Schéma pro osobu s tituly a se jménem a příjmením v libovolném pořadí
w
x
s<xs:element name="osoba">
<xs:complexType>
<xs:sequence>
<xs:element name="titul" type="xs:string" minOccurs="0"/>
<xs:choice>
<xs:sequence>
<xs:element name="jméno" type="xs:string"/>
<xs:element name="příjmení" type="xs:string"/>
</xs:sequence>
<xs:sequence>
<xs:element name="příjmení" type="xs:string"/>
<xs:element name="jméno" type="xs:string"/>
</xs:sequence>
</xs:choice>
<xs:element name="titul" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>V textově zaměřených dokumentech jako jsou různé články,
dokumentace apod. často potřebujeme používat tzv. smíšený obsah (mixed
content). Je to situace, kdy může být text na stejné úrovni kombinován
s dalšími elementy. Typickým příkladem je element pro odstavec.
Ten může obsahovat jak přímo text, tak i další elementy pro
zvýraznění textu, vytváření odkazů apod. Definice smíšeného obsahu je
velmi jednoduchá. U definice komplexního typu elementu stačí
přidat atribut mixed
s hodnotou true.
Smíšený obsah v XML schématech funguje poněkud odlišně než
v DTD. V DTD smíšený obsah vždy automaticky implikuje, že
elementy na stejné úrovni jako text se mohou vyskytovat
v libovolném pořadí a opakovaně. V XML schématech smíšený
obsah říká, že text se může objevit kdekoliv mezi elementy, které
komplexní typ definuje. Smíšený obsah v klasickém slova smyslu se
proto musí definovat pomocí skupiny choice, která má libovolný počet výskytů a obalí
všechny elementy, které se mohou mísit s textem.
Příklad 3.5. Smíšený obsah
<odstavec>Odstavce typicky obsahují <pojem>smíšený obsah</pojem>. Text se může střídat s <odkaz url="http://www.kosek.cz">odkazy</odkaz> a dalšími <pojem>elementy</pojem>.</odstavec> <odstavec>Odstavec může obsahovat i jen text.</odstavec> <odstavec><pojem>Nebo jen element.</pojem></odstavec>
w
x
s<xs:element name="odstavec">
<xs:complexType mixed="true">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="pojem" type="xs:string"/>
<xs:element name="odkaz">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="url" type="xs:anyURI"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>Prázdné elementy jsou takové elementy, které nemají žádný obsah. V instanci dokumentu se zapisují jedním z následujících dvou způsobů:
<prazdny/> <prazdny></prazdny>
Prázdné elementy mají obvykle atributy. Atributy se musí
definovat jako součást komplexního typu. Např. pro element
img s atributem src
<img src="obrazek.png"/>
by definice elementu vypadala následovně
w
x
s<xs:element name="img">
<xs:complexType>
<xs:attribute name="src" type="xs:anyURI"/>
</xs:complexType>
</xs:element>Jedná se přitom o zkrácený zápis. Plná syntaxe (kterou samozřejmě nemusíte používat) vychází z idey, že elementu odebereme jeho obsah a přidáme k němu atribut.
w
x
s<xs:element name="img">
<xs:complexType>
<xs:complexContent>
<xs:restriction base="xs:anyType">
<xs:attribute name="src" type="xs:anyURI"/>
</xs:restriction>
<xs:complexContent>
</xs:complexType>
</xs:element>Atribut je součástí komplexního typu a definuje se pomocí
elementu attribute:
w
x
s<xsl:attribute name="vek" type="xs:positiveInteger"/>U atributů můžeme určit nejen jejich jméno a typ, ale zda mají být povinné, jaké je jejich defaultní hodnota apod. Např.:
w
x
s<xs:attribute name="ident" type"xs:ID" use="required"/>
<xs:attribute name="měna" type="xs:string" default="USD"/>Atributy se uvádějí vždy až na konci komplexního typu. Jediným
složitějším případem je situace, kdy chceme atributy přidat
k elementu, který neobsahuje již další podelementy, ale má
jednoduchý datový typ. Výsledný zápis je pak poněkud krkolomný.
Představme si, že chceme definovat element cena,
který bude mít atribut měna pro uložení kódu měny.
Chceme přitom využít dříve definované datové typy. V řeči XML
schémat vypadá definice následovně:
w
x
s<xs:element name="cena">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="částkaType">
<xs:attribute name="měna" type="kódMěnyType"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>Přeloženo do lidské řeči: Vytvoř komplexní typ, který vznikne
rozšířením jednoduchého obsahu s typem
částkaType o atribut měna.
Bývá dnes dobrým zvykem přiřadit každému nově vytvořenému
značkovacímu jazyku vlastní jmenný prostor, aby jej šlo snadno
identifikovat. Jmenný prostor, do něhož budou elementy patřit, se
určuje pomocí atributu targetNamespace u kořenového elementu
schématu. Z praktických důvodů se tento jmenný prostor obvykle
deklaruje i jako implicitní, abychom před naše elementy nemuseli
všude psát nějaký prefix.
XML schéma má jednu nepříjemnou vlastnost – do jmenného
prostoru patří jen globální elementy. Jsou to pouze ty elementy, které
jsou ve schématu definované na nejvyšší úrovni přímo pod elementem
schema. Předpokládejme například
následující schéma:
w
x
s<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:x-kosek:schemas:pokus"
xmlns="urn:x-kosek:schemas:pokus">
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:element name="b" type="xs:string"/>
<xs:element name="c" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>Instance, která schématu vyhovuje, pak odporuje ustáleným praktikám pro tvorbu dokumentů XML. Můžeme ji zapsat buď jako:
<a xmlns="urn:x-kosek:schemas:pokus"> <b xmlns="">foo</b> <c xmlns="">bar</c> </a>
nebo
<p:a xmlns:p="urn:x-kosek:schemas:pokus"> <b>foo</b> <c>bar</c> </p:a>
Nepřehledný a nekonzistentní zápis je právě důsledkem toho, že
elementy b a c nepatří do
žádného jmenného prostoru. Každý element by přitom měl patřit do
nějakého jmenného prostoru. Toho můžeme dosáhnout tím, že pomocí
atributu elementFormDefault řekneme, že celé schéma
musí používat kvalifikované (rozuměj zařazené do nějakého jmenného
prostoru) elementy.
w
x
s<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:x-kosek:schemas:pokus"
xmlns="urn:x-kosek:schemas:pokus"
elementFormDefault="qualified">
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:element name="b" type="xs:string"/>
<xs:element name="c" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>S tímto schématem už můžeme vytvářet dokumenty, tak jak je zvykem.
<a xmlns="urn:x-kosek:schemas:pokus"> <b>foo</b> <c>bar</c> </a> <p:a xmlns:p="urn:x-kosek:schemas:pokus"> <p:b>foo</p:b> <p:c>bar</p:c> </p:a>
Ve skutečnosti je možné u každého elementu nebo atributu určit,
zda má nebo nemá patřit do cílového jmenného prostoru. Slouží k tomu
atribut form, jehož
použití ukazuje následující příklad, který rovněž definuje všechny tři
elementy v jednom jmenném prostoru.
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:x-kosek:schemas:pokus"
xmlns="urn:x-kosek:schemas:pokus">
<xs:element name="a">
<xs:complexType>
<xs:sequence>
<xs:element name="b" type="xs:string" form="qualified"/>
<xs:element name="c" type="xs:string" form="qualified"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>V praxi se však příslušenství elementů (případně atributů) do
jmenného prosotoru obvykle definuje globálně a využívají se proto
atributy elementFormDefault a attributeFormDefault.
Chceme-li přímo v dokumentu XML určit, kde může parser
najít schéma například pro účely validace, musíme k tomu použít
speciální globální atributy patřící do jmenného prostoru
http://www.w3.org/2001/XMLSchema-instance.
Nepoužíváme-li jmenné prostory, stačí do atributu noNamespaceSchemaLocation uvést
URL adresu, na které se nachází schéma. Můžeme použít samozřejmě
i relativní URL a jednoduše se tak odkázat na soubor se
schématem, který je ve stejném adresáři jako dokument.
<dokument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="dokument.xsd"> ... </dokument>
Používáme-li jmenné prostory, musíme použít atribut schemaLocation. Ten může obsahovat několik
dvojic hodnot URI jmenného prostoru a umístění schématu. Hodnoty jsou
přitom oddělené mezerami nebo jinými bílými znaky.
<dokument
xmlns="urn:x-kosek:schemas:dokument:1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:x-kosek:schemas:dokument:1.0
dokument.xsd">
...
</dokument>Postupem času se vyvinulo několik přístupů k tomu, jak správně vystavět schéma. Problém, který je potřeba rozhodnout, spočívá ve výběru, kdy používat lokální, a kdy globální elementy, jak moc bude schéma rozšiřitelné a použitelné v dalších schématech.
První přístup je označován jako matrjóška, tedy ruská panenka, kdy do sebe zapadají jednotlivé menší a menší panenky. V tomto přístupu je globální jen jeden element a všechny ostatní jsou uvnitř něj definovány jako lokální.
w
x
s<xs:element name="zamestnanec">
<xs:complexType>
<xs:sequence>
<xs:element name="jmeno" type="xs:string"/>
<xs:element name="prijmeni" type="xs:string"/>
<xs:element name="plat" type="xs:decimal"/>
</xs:sequence>
</xs:complexType>
</xs:element>Instance tohoto schématu může začínat jen elementem
zamestnanec, a jiný element ani nejde znovu
použít v jiných schématech, které by toto schéma importovaly.
Výhodou je naopak krátké a kompaktní schéma, které je rychle
napsané.
Druhý častý přístup se nazývá salámová kolečka. Všechny elementy jsou definovány jako globální a pak jsou složeny dohromady pomocí odkazů.
w
x
s<xs:element name="jmeno" type="xs:string"/>
<xs:element name="prijmeni" type="xs:string"/>
<xs:element name="plat" type="xs:decimal"/>
<xs:element name="zamestnanec">
<xs:complexType>
<xs:sequence>
<xs:element ref="jmeno"/>
<xs:element ref="prijmeni"/>
<xs:element ref="plat"/>
</xs:sequence>
</xs:complexType>
</xs:element>Dokument může začínat libovolným elementem, libovolný element můžeme znovu použít. Nevýhoda je, že v tomto přístupu nejde pro element stejného názvu definovat dva různé modely obsahu v závislosti na kontextu jeho výskytu. Tuto možnost první způsob nabízí.
Třetí metoda, označovaná jako metoda slepého Benátčana, pro všechny elementy nejprve definuje typy, které lze znovu používat. Elementy jsou pak definovány lokálně pomocí těchto typů, takže mohou mít při shodě jmen různé modely obsahu. Tento přístup nabízí nejvíce možností a komfortu, a ve většině případů je nejvhodnější. Jeho nevýhodou je větší pracnost a složitost v porovnání s předchozími metodami.
w
x
s<xs:simpleType name="jmenoType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="15"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="prijmeniType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="20"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="platType">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="zamestnanecType">
<xs:sequence>
<xs:element name="jmeno" type="jmenoType"/>
<xs:element name="prijmeni" type="prijmeniType"/>
<xs:element name="plat" type="platType"/>
</xs:sequence>
</xs:complexType>
<xs:element name="zamestnanec" type="zamestnanecType"/>Velkou nevýhodou XML z pohledu databázistů byla neexistence standardního mechanismu pro zaznamenání toho, že nějaký element obsahuje nedefinovanou hodnotu (NULL). Oba XML zápisy
<autor></autor> <autor/>
můžeme chápat jako element autor, který
obsahuje hodnotu prázdný řetězec. Pro zachycení hodnoty NULL můžeme
použít další globální atribut (používají se i pro připojení
schématu):
<autor xsi:nil="true"></autor> <autor xsi:nil="true"/>
To, že element může nabývat prázdnou hodnotu, musíme předtím definovat ve schématu:
w
x
s<xs:element name="autor" nillable="true" type="xs:string"/>Chceme-li zabránit výskytu duplicitních hodnot v nějaké množině elementů nebo atributů, můžeme si definovat unikátní klíč. Klíčů může být v jednom schématu definováno několik a pro jejich definici se používá dotazovací jazyk XPath.
Použití klíče si ukážeme na příkladě. Předpokládejme, že
v následujícím seznamu zaměstnanců chceme zajistit unikátnost
jejich osobních čísel (atribut oc).
<zamestnanci>
<zamestnanec oc="1164">
<jmeno>Procházka Karel</jmeno>
<sef>2021</sef>
</zamestnanec>
<zamestnanec oc="1168">
<jmeno>Novotná Alena</jmeno>
<sef>2021</sef>
</zamestnanec>
<zamestnanec oc="1230">
<jmeno>Klíma Josef</jmeno>
<sef>1168</sef>
</zamestnanec>
<zamestnanec oc="1564">
<jmeno>Pinkas Josef</jmeno>
<sef>2021</sef>
</zamestnanec>
<zamestnanec oc="2021">
<jmeno>Kládová Adéla</jmeno>
</zamestnanec>
</zamestnanci>Klíč proto definujeme u elementu
zamestnanci, který obsahuje jednotlivé zaměstnance,
pro které se má dodržovat unikátnost osobního čísla.
w
x
s<xs:element name="zamestnanci">
<xs:complexType>
<xs:sequence>
<xs:element ref="zamestnanec" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:unique name="oc_je_unikatni">
<xs:selector xpath="zamestnanec"/>
<xs:field xpath="@oc" />
</xs:unique>
</xs:element>Důležité je, aby definice klíče byla umístěna v definici elementu, který pod sebou jako potomky obsahuje elementy, pro které se referenční integrita hlídá. V opačném případě nebude klíč fungovat správně.
Klíč definovaný pomocí unique
kontroluje pouze unikátnost hodnot, ale neohlásí chybu, pokud
u některého elementu klíč chybí. Chceme-li mít zaručeno, že
hodnoty jsou unikátní a jsou uvedeny všude, můžeme použít element
key.
Podobně jako v relační databázi, můžeme i na půdě jednoho dokumentu XML definovat referenční integritu. Postup je obdobný jako u databází – definujeme si klíče a pak cizí klíče, které na ně ukazují. Následující ukázka ilustruje omezení, které zajistí, že pro každého zaměstnance bude existovat jeho šéf.
w
x
s<xs:element name="zamestnanci">
<xs:complexType>
<xs:sequence>
<xs:element ref="zamestnanec" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:key name="osobni_cislo">
<xs:selector xpath="zamestnanec"/>
<xs:field xpath="@oc"/>
</xs:key>
<xs:keyref name="sef_je_existujici_oc"
refer="osobni_cislo">
<xs:selector xpath="zamestnanec"/>
<xs:field xpath="sef"/>
</xs:keyref>
</xs:element>Při návrhu schémat máme k dispozici několik vlastností, které jsou hodně podobné vlastnostem známým z objektově orientovaných jazyků. Možnost odvozování nových typů na základě již existujících není nic jiného než dědičnost. Odvozování jde přitom použít i pro komplexní typy, ne jen pro jednoduché, jak jsme si ukázali.
Mezi další objektově inspirované rysy patří substituční skupiny, abstraktní datové typy a možnost zablokovat další dědění od nějakého datového typu.
Máme-li jednou velké schéma a chceme jej rozdělit do více
menších souborů pro lepší přehlednost, můžeme výsledné schéma složit
z několika částí pomocí elementu include.
<xs:include schemaLocation="cast-schematu.xsd"/>
Chceme-li definovat schéma dokumentu, který se skládá
z elementů v různých jmenných prostorech, využijeme naopak
elementu import. Ten umožňuje do
schématu načíst definice elementů a typů patřících do jiného jmenného
prostoru. Následující příklad ukazuje import schématu jazyka XHTML do
našeho schématu.
Příklad 3.6. Schéma importují schéma pro XHTML –
wxs/message.xsd
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:html="http://www.w3.org/1999/xhtml">
<xs:import namespace="http://www.w3.org/1999/xhtml"
schemaLocation="xhtml11/xhtml11.xsd"/>
<xs:element name="message">
<xs:complexType>
<xs:sequence>
<xs:element name="from" type="xs:string"/>
<xs:element name="to" type="xs:string"/>
<xs:element name="subject" type="xs:string"/>
<xs:choice>
<xs:element name="body" type="xs:string"/>
<xs:element ref="html:body"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>Příklad 3.7. Zpráva s textovým tělem –
wxs/zprava1.xml
<?xml version="1.0" encoding="UTF-8"?> <message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="message.xsd"> <from>adam@example.org</from> <to>eva@example.org</to> <subject>Pokusný email v textu</subject> <body>Tělo e-mailu.</body> </message>
Příklad 3.8. Zpráva s XHTML tělem –
wxs/zprava2.xml
<?xml version="1.0" encoding="UTF-8"?>
<message xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="message.xsd">
<from>adam@example.org</from>
<to>eva@example.org</to>
<subject>Pokusný email v textu</subject>
<body xmlns="http://www.w3.org/1999/xhtml">
<h1>E-mail zapsaný v HTML</h1>
<p>První odstavec.</p>
<p>Druhý odstavec</p>
<table>
<tr><th>A</th><th>B</th></tr>
<tr><td>2</td><td>7</td></tr>
</table>
</body>
</message>XML schéma umožňuje k libovolné jeho části připojit dokumentaci. Ta může obsahovat buď prostý text, nebo můžeme použít v odpovídajícím jmenném prostoru nějaký speciální značkovací jazyk jako XHTML nebo DocBook.
w
x
s<xs:element name="zamestnanec">
<xs:annotation>
<xs:documentation>Element slouží pro uchování důležitých
údajů o zaměstnanci.</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="jmeno" type="xs:string"/>
<xs:element name="prijmeni" type="xs:string"/>
<xs:element name="plat" type="xs:decimal"/>
<xs:element name="narozen" type="xs:date"/>
</xs:sequence>
</xs:complexType>
</xs:element>Existují nástroje, které jsou pak schopné na základě schématu a v něm obsažených komentářů vygenerovat přehlednou dokumentaci. Jedním z těchto nástrojů je xs3p dostupné na adrese http://titanium.dstc.edu.au/xml/xs3p/index.shtml. Jedná se o XSLT styl, který umí schéma převést do webové stránky.
Vygenerování dokumentace tak spočívá v prostém aplikování XSLT stylu na schéma:
saxon -o faktura.html faktura.xsd ../tools/xs3p.xslPodobným způsobem lze do elementu appInfo přidat i různé aplikačně závislé
informace. Třeba doplňkovou validaci pomocí Schematronu.
[2] Skutečnost je taková, že all může být použito
jen přímo uvnitř komplexního typu a nemůže být kombinováno
s dalšími konstrukcemi jako sequence a
choice.