XML jest językiem, dużo więcej daje jednak spojrzenie na XML jako metajęzyk, to znaczy zbiór języków, zastosowań XML. Na przykład zastosowaniem XML jest XHTML 1.0, to znaczy każdy dokument XHTML jest dokumentem XML, a dodatkowo jest zgodny z defnicją typu dokumentu XHTML.
Do zastosowań XML należą znane standardy, jak XSLT, XHTML czy WSDL. Można także zdefiniować swój własny język – zastosowanie XML. Definiowanie zastosowania XML nazywa się zwykle definiowaniem struktury dokumentu lub typu dokumentu.
Porządna definicja zastosowania XML, analogicznie do definicji języka programowania, powinna zawierać dwie części:
W praktyce formalnie definiuje się jedynie część składniową, służą do tego wymienione poniżej standardy. Natomiast znaczenie elementów i atrybutów opisuje się słownie w oddzielnym dokumencie tekstowym (jak w przypadku rekomendacji W3C), w komentarzach DTD czy schematu, lub niestety nie opisuje w ogóle.
Zalecane sposoby definiowania struktury dokumentu:
Alternatywne standardy (jako ciekawostka):
Obecnie najbardziej zalecanym standardem do definiowania nowych typów dokumentów jest XML Schema. Istnieje jednak wiele typów dokumentów już zdefiniowanych za pomocą DTD.
Każdy dokument XML musi być poprawny składniowo (well-formed) – być zgodny z gramatyką XML i spełniać well-formedness constraints wymienione w rekomendacji XML.
Dodatkowo dokument XML może być poprawny strukturalnie (valid) – być zgodny z definicją struktury. Jeśli nie jest powiedziane inaczej, chodzi o DTD, a dokument ma spełniać validity constraints wymienione w rekomendacji XML. Tych samych terminów używa się także do określenia zgodności ze schematem XML Schema bądź innych standardów.
Proces sprawdzania poprawności strukturalnej nazywa się walidacją. Po wykonaniu walidacji mamy pewność, że dokument posiada właściwą strukturę. Dzięki temu, np. we własnych programach czy w arkuszach XSLT, nie musimy już rozważać przypadków błędnych i od razu wiemy, że np. wymagane elementy rzeczywiście występują.
Standard XML Schema zdefiniowany jest w trzech rekomendacjach:
Być może zamiast mojego, wolicie profesjonalny tutorial po XML Schema...
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:
Ponadto na początku dokumentu mogą wystąpić elementy związane z łączeniem wielu schematów, a w dowolnym miejscu anotacje, które powinny stanowić dokumentację schematu i, jeśli nie ma tego w osobnym dokumencie, określać znaczenie poszczególnych elementów i atrybutów.
W schematach dla elementów i atrybutów o różnych nazwach określa się typ zawartości. Można to zrobić w poniższy sposób, odwołując się do typu nazwanego.
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.
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 o tym za 2 tygodnie).
Definicję typu można umieścić też wewnątrz definicji elementu (lub atrybutu). Wówczas mamy do czynienia z typem anonimowym.
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 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.
W definicji typu złożonego określa się jakie podelementy i atrybuty może zawierać element o tym typie.
Na przykład elementy o poniższym typie mogą zawierać dowolną 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
.
<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.
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 < unbounded
.
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
.
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>
Otwórz w widoku tekstowym XML Spy ten schemat. Spróuj zdefiniować następujące typy zawartości:
A
,A
,
dowlonie wymieszana z dowolną liczbą elementów B
,A
, jeden element B
i opcjonalny element C
,
wszystko w dowolnej kolejności.
Wyjaśnienie: Program XML Spy sprawdza czy typy zawartości w schemacie są deterministyczne.
Nie jest to wymagane w ogólności dla XML Schema, ale jest zalecane dla DTD. Prawdopodobnie stąd to wymaganie w XML Spy.
W grupach typu all
elementy mogą występować najwyżej jeden raz.
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.
Napisz wizytówkę zgodną ze swoim schematem i zwaliduj ją.
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.
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>
Podobnie jak grupy elementów, można także zdefiniować grupy atrybutów i odwoływać się do nich w definicjach typów.
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>
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 upraszczając znakowany tekst.
Typ zawartości, który dopuszcza tekst z zanurzonymi elementami, nazywa się typem mieszanymmixed 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).
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>
Zapisz schemat opisujący znane z HTML elementy dla list ol
, ul
, li
oraz kilka przykładowych elementów.
Załóżmy, że body
może zawierać tekst z zanurzonymi w nim listami, listy tylko elementy wypunktowania (li
),
a elementy wypunktowania znowu tekst z zanurzonymi listami. W każdym miejscu gdzie dopuszczalny jest tekst,
może być on znakowany elementami font
z atrybutami size
i face
.
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.
Przykładami schematów napisanych w ten sposób są osoby1.xsd, osoby2.xsd i przyklad2.xsd.
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ł).
Schemat jest czytelniejszy, 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.
Przykładami schematów napisanych w ten sposób są osoby3.xsd, osoby4.xsd i przyklad3.xsd.
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. Dokument przyklad2.xml
jest zgodny z tym schematem.
Natomiast w schemacie przyklad3.xsd element c
zdefiniowany jest lokalnie dwa razy. Wewnątrz elementów a
dopuszczalna jest zawartość A
i AAA
,
wewnątrz elementów b
dopuszczalna jest zawartość B
i BBB
.
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.
Jak w przykładzie z poprzednich zajęć, 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 (nawet bez 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 przypadkach.
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.
W programie XML Spy schematy można tworzyć w wizualnym edytorze (prezentacja na ćwiczeniach). Szczególnie czytelne jest wizualne tworzenie typów złożonych. Jest to wygodne, jednak nie zwalnia autora z dbałości o czytelność i estetykę schematu zapisanego tekstowo.
XML Spy domyślnie tworzy typy anonimowe wewnątrz definicji elementów i atrybutów, z podelementami definiowanymi lokalnie. Schemat po prostu "wyklikany" jest zazwyczaj bardzo zagnieżdżony. I nie definiuje atrybutów, tylko same elementy :).
Edytor wizualny pozwala jednak na definiowanie typów nazwanych, rozszerzanie i zawężanie typów, definiowanie grup oraz globalne definicje elementów i atrybutów (i referencje do nich w innych definicjach typów). Należy z tych możliwości korzystać, aby schematy były modularne (bez redundancji, łatwo rozszerzalne), estetyczne i czytelne.