Polsko-Japońska Wyższa Szkoła Technik Komputerowych Technologie Internetu wykład 9: XML Schema Piotr Habela Polsko-Japońska Wyższa Szkoła Technik Komputerowych 1
W poprzednim odcinku… Zastosowanie definicji typów dokumentów; Rozwiązania DTD (Document Type Definition): Definicja zewnętrzna lub wewnętrzna; Rodzaje odwołań do zasobów zewnętrznych; Elementy proste (znakowe, puste, dowolne); Modele zawartości elementowej (liczności, wybór, sekwencje); Deklaracje elementów: dostępne typy oraz wartości domyślne; Rodzaje encji (entities) w XML; Rola przestrzeni nazwowych (namespaces); Ograniczenia DTD. XML Schema: Rozbudowany system typów; Obsługa przestrzeni nazwowych. 2
Plan wykładu Budowa dokumentu XML Schema: Najważniejsze możliwości XML Schema; Deklarowanie atrybutów i zawartości elementowej; Typy wbudowane XML Schema; Tworzenie typów pochodnych: poprzez ograniczenia i poprzez rozszerzenia; Sposoby organizowania podelementów; Ograniczenia unikalności; … Język XPath: Sposoby nawigacji; Skróty; Predykaty; Dostępne operacje. 3
XML Schema – podstawowe informacje Instance document oznacza dokument zbudowany zgodnie z daną specyfikacją schematu. Dokument-wystąpienie nie musi obowiązkowo odwoływać się do swojej specyfikacji schematu. Schematy XML Schema są zwykle umieszczane w odrębnych plikach – zwyczajowo z rozszerzeniem .xsd. Ten skrót jest również używany do oznaczenia przestrzeni nazwowej (namespace), obejmującej standardowe elementy XML Schema. Definicja schematu zawarta jest w elemencie-korzeniu XML o nazwie schema: <xsd:schema> … </xsd:schema> Deklaracja elementu oraz atrybutu (lokalnie wewnątrz odpowiednich elementów lub globalnie, celem wielokrotnego użycia): <xsd:element name=“nazwaElementu“ type=”typElementu” /> <xsd:attribute name=“nazwaAtrybutu“ type=”typAtrybutu” /> 4
Składniki definicji schematu Obok deklaracji elementów i atrybutów, istnieje możliwość globalnego zdefiniowania: własnego typu prostego. Wówczas zawartością definicji typu jest ograniczenie któregoś z wbudowanych typów prostych (zob. dalej): <xsd:simpleType name="IdentProduktu"> . . . </xsd:simpleType> … oraz typu złożonego. W tym wypadku wewnątrz mogą znaleźć się deklaracje podelementów oraz atrybutów. <xsd:complexType name="DaneAdresowe"> . . . </xsd:complexType> 5
Kompozycyjność definicji schematów Deklaracje atrybutów mogą występować w ramach definicji typu złożonego albo w ramach definicji elementu. Deklaracje elementów mogą występować globalnie, w ramach definicji typu złożonego albo w ramach definicji innego elementu. Definicje typów mogą wystąpić globalnie (z nadaniem nazwy atrybutem name) albo wewnątrz deklaracji opisywanego przezeń elementu (anonimowo, celem jednorazowego wykorzystania w miejscu definicji); Zwróćmy uwagę na to kluczowe rozróżnienie: Definicje typów tworzą nowe typy dostępne do wykorzystania; Deklaracje elementów określają nazwy i typy elementów i atrybutów, które mogą pojawić się w odpowiednich miejscach dokumentu. 6
Definicje typów a elementy złożone Definicja typu – umieszczona w korzeniu schematu: można się doń potem odnosić w deklaracjach elementów w sposób analogiczny jak dla atrybutów prostych => czytelniejsze deklaracje oraz ponowne użycie definicji typów; Definicja typu złożonego: konstruowana elementem xsd:complexType; może zawierać deklaracje elementów, atrybutów oraz referencji do elementów; nie może być użyta w deklaracjach atrybutów; Deklaracja elementu poprzez podanie typu: Podanie w atrybucie type nazwy globalnie zdefiniowanego typu; Deklaracja poprzez referencję: powoduje wstawienie do bieżącej deklaracji globalnie zadeklarowanego elementu o podanej nazwie: <xsd:element ref="adres" minOccurs="0"/> 7
Deklarowanie zawartości elementowej Zarówno w definicjach globalnych typów jak i w deklaracjach elementów złożonych, podelementy definiujemy wewnątrz elementu xsd:complexType. Ograniczenia na liczbę wystąpień podelementów: domyślnie dolną a zarazem górną granicą liczności jest „dokładnie 1”; liczności te można modyfikować za pomocą atrybutów: elementu minOccurs i maxOccurs, których wartościami muszą być liczby naturalne. Wartością specjalną dla atrybutu maxOccurs może być ponadto ”unbounded”. Deklaracje liczności nie są dozwolone dla elementów globalnych. 8
Deklarowanie atrybutów Atrybuty (poza globalnymi) deklarujemy wewnątrz elementu xsd:complexType. Mogą posiadać wyłącznie typy proste. Występowanie atrybutu określone jest atrybutem (tj. atrybutem w elemencie xsd:attribute) use, mogący przybierać wartość: ”optional”, ”required” lub ”prohibited”. Domyślną deklaracją (przy pominięciu) jest ”optional”. Domyślną wartość atrybutu można przypisać stosując atrybut defalut. Atrybut use musi wówczas przybrać wartość ”optional”. Wymuszenie stałej wartości atrybutu osiągamy poprzez zastosowanie w jego deklaracji atrybutu fixed=”wartość”. Wówczas, jeśli atrybut jest opcjonalny, wymagana jest albo jego nieobecność w wystąpieniu albo jego wystąpienie ze zgodną wartością. Atrybut default stosuje się też w deklaracjach elementów. Tu jednak podstawienie wystąpi tylko wtedy, gdy w dokumencie dany element pojawi się bez zawartości (jeśli nie wystąpi w ogóle, to do podstawienia nie dojdzie). 9
Typy wbudowane Specyfikacja określa wiele wbudowanych typów prostych. Część z nich zdefiniowano poprzez nałożenie restrykcji na definicje bardziej podstawowe. Niektóre typy wbudowane: string, normalizedString, token, byte, unsignedByte positiveInteger, negativeInteger, nonPositiveInteger, nonNegativeInteger integer, long, decimal, float, boolean base64Binary -> każde 6 bitów kodowane symbolem alfanumerycznym; time - np. 13:20:00.000, 13:20:00.000-05:00 dateTime - np. 1999-05-31T13:20:00.000-05:00 duration - np. P1Y2M3DT10H30M12.3S date (oraz ich wycinki) - np. 1999-05-31 anyURI - np. http::/www.w3c.org/ language - np. en-US, pl ID, IDREF, IDREFS, NOTATION… 10
Definiowanie typów pochodnych przez ograniczenia Tworzeniu pochodnych typów prostych poprzez nakładanie ograniczeń służy element xsd:restriction, umieszczany wewnątrz elementu xsd:simpleType. Element restriction zawiera atrybut base, określający typ bazowy definicji. Może zawierać szereg podelementów, określanych jako constraining facets (aspekty ograniczające). Np. dla typów liczbowych można użyć elementów xsd:maxExclusive i xsd:minInclusive z atrybutem value=”warośćLiczbowa”. Np. <xsd:simpleType name="numerWewnetrzny"> <xsd:restriction base="xsd:integer"> <xsd:minInclusive value="10000"/> <xsd:maxInclusive value="99999"/> </xsd:restriction> </xsd:simpleType> 11
Inne aspekty ograniczające (1) Dla atrybutów xsd:string i pochodnych można sformułować wzorzec przy użyciu wyrażeń regularnych: umieszczamy podelement xsd:pattern z atrybutem value=”wyrażenieRegularne”. Np. "\d{3}-[A-Z]{2}" oznacza trzy cyfry dziesiętne, myślnik oraz dwie wielkie litery. <xsd:simpleType name="kodProduktu"> <xsd:restriction base="xsd:string"> <xsd:pattern value="\d{3}-[A-Z]{2}"/> </xsd:restriction> </xsd:simpleType> Wyrażenia regularne posiadają bardzo bogate możliwości. M. in. można odwoływać się do różnych kategorii znaków Unicode. 12
Inne aspekty ograniczające (2) Kolejną możliwością jest podanie dozwolonych wartości danego typu w postaci wyliczenia (enumeration). Służą temu podelementy xsd:enumeration z atrybutami value: <xsd:restriction base="xsd:string"> <xsd:enumeration value=„PLN"/> <xsd:enumeration value=„USD"/> </xsd:restriction> Dla typów liczbowych można określać łączną liczbę cyfr oraz liczbę cyfr po przecinku: xsd:totalDigits, xsd:fractionDigits Można określać długość albo jej limity: maksymalny i minimalny: length, albo maxLength i minLength Dla typu string oznaczać to będzie liczbę znaków (np. kody walut), dla typów binarnych – liczbę oktetów, zaś dla list – ich rozmiar. 13
Właściwości aspektów ograniczających Łączenie aspektów ograniczających: aspekty wzorca współistniejące dla danej definicji z typem wyliczeniowym są traktowane alternatywnie (suma logiczna); inne kombinacje aspektów ograniczających stanowią warunek w postaci iloczynu logicznego. Blokowanie właściwości aspektów – uniemożliwi redefinicję danego ograniczenia w typie pochodnym: fixed=”true”; Ograniczenia można też stosować do typów złożonych. W tym wypadku wyprowadzanie poprzez zawężenie liczby wystąpień wymaga ponownego podania całej ich zawartości. Jednakże korzyścią pozostaje w takim wypadku zgodność typologiczna z nadtypem. 14
Typy pochodne – pozostałe możliwości Rozszerzenia: przypominają specjalizację klas w językach programowania. Składnia analogiczna jak w wypadku ograniczeń. Zamiast xsd:constraint stosuje się xsd:extension: <xsd:extension base=”typBazowy”> . . . </xsd:extension> Wprowadzone w ramach rozszerzenia podelementy muszą być w wystąpieniach dołączane na końcu. Blokowanie wyprowadzeń (deklaracje atrybutem final): Można zabronić wyprowadzania typów, umieszczając w ich definicji atrybut final z wartością ”#restriction”, ”#extension” lub ”#all”. 15
Organizacja podelementów Podelementy mogą być grupowane (za pomocą znaczników zwanych compositors) w kompozycje trzech rodzajów: Sekwencja: wszystkie podelementy muszą wystąpić dokładnie w podanej kolejności: <xsd:sequence> . . . </xsd:sequence> Wybór: musi wystąpić dokładnie jeden z wymienionych podelmentów: <xsd:choice> . . . </xsd:choice> Zbiór: wszystkie zawarte elementy muszą wystąpić, jednakże ich kolejność jest dowolna <xsd:all> . . . </xsd:all> Kompozycje choice i sequence mogą być wzajemnie zagnieżdżane. Można też określać dla nich liczności (minOccurs i maxOccurs). Ograniczenia kompozycji all: Tutaj dozwoloną licznością jest tylko 1 (domyślnie) albo 0..1. Nie można zagnieżdżać w nim innych kompozycji. Nie może też być zagnieżdżany w innej kompozycji. Stosowanie w rozszerzeniach – tylko gdy element bazowy jest pusty. 16
Zawartość prosta z atrybutami Musimy użyć complexType (a nie simpleType), gdyż ten pierwszy nie pozwala na zdefiniowanie atrybutów. Wewnątrz complexType umieszczamy: <xsd:simpleContent> . . . </xsd:simpleContent> Zawartość prosta takiego elementu musi zostać wprowadzona poprzez rozszerzenie (xsd:extension), atrybutem base: base=”nazwaTypuProstego”. Wówczas z typu bazowego pochodzi określenie zawartości elementu (np. xsd:string), zaś rozszerzenie wprowadza potrzebne nam atrybuty. 17
Grupy elementów i atrybutów Ograniczają nadmiarowość definicji schematu. Zastosowanie (ograniczenie nadmiarowości i uczynienie schematów bardziej przejrzystymi) odpowiada encjom parametrycznym z DTD. Do zadeklarowania grupy stosowane są elementy odpowiednio: <xsd:group> … </xsd:group> oraz <xsd:attributeGroup> ... </xsd:attributeGroup>, posiadające atrybut name. Odwołaniom do takich grup służą te same elementy występujące w postaci elementów pustych z atrybutem ref. 18
Ograniczenia unikalności (1) W wypadku DTD jedynym (mało elastycznym) środkiem zapewnienia unikalności było użycie typu ID. Ograniczenia unikalności deklaruje się na końcu deklaracji elementu, którego dotyczą, albo w elementach nadrzędnych (najczęściej umieszczane są w końcu elementu korzenia (xsd:schema). Postać ograniczenia: <xsd:key name=”nazwaNaszegoKlucza”> <xsd:selector xpath=”elementwRamachKtoregoZapewniamyUnikalnosc” /> <xsd:field xpath=”poleKlucza”/> <!-- atrybut lub podelement --> … <!-- klucz może być złożony --> </xsd:key> 19
Ograniczenia unikalności (2) Selektory i pola wskazujemy używając atrybutu xpath. Jak sugeruje nazwa, identyfikuje on zawartość dokumentu XML w sposób określony specyfikacją XPath. Jak zobaczymy, ograniczenia na miejsce występowania deklaracji unikalności wynikają właśnie z założeń specyfikacji XPath. Podobne do ograniczenia xsd:key jest xsd:unique. To drugie odróżnia się dopuszczalnością pustych elementów (i niepowtarzalność sprawdza się wówczas tylko pośród niepustych elementów). Zarówno ograniczenie unikalności jak i ograniczenie klucza mogą dotyczyć wartości złożonych. Integralność referencyjna – określa zależność klucza obcego. Przedmiotem odwołania musi być wartość zadeklarowana jako klucz. Deklarowane za pomocą elementu keyref: <xsd:keyref refer=”nazwaKlucza”> . . . </xsd:keyref> Atrybut refer określa, do jakiego klucza odwołują się referencje. Selektor i pole (lub pola) są deklarowane analogicznie jak w wypadku kluczy i wartości unikalnych. 20
Definiowanie list Zawartością elementu a także atrybutu może być kolekcja wartości typu prostego, rozdzielona znakami białymi. Tego rodzaju zawartość deklaruje się jako listę (list) określonego typu bazowego. Listy nie mogą składać się z innych list. Deklaracje – za pomocą elementu xsd:list: <xsd:list itemType=”nazwaTypuZawartosci” /> Listy mogą składać się jedynie z wartości (jednego) typu prostego. 21
Wariantowe definicje typów (unie) Dopuszczalne wartości typów prostych można definiować jako sumę wartości dopuszczanych przez dwa lub więcej typów. Deklaracji takich typów służą unie (unions). Wewnątrz elementu <xsd:union> . . . </xsd:union> umieszczane są dwie lub więcej definicje typu; Jeśli zaś potrzebne typy zostały już zadeklarowane globalnie, można się odwołać do ich nazw w atrybucie memberTypes: <xsd:union memberTypes=”nazwaTypu1 nazwaTypu2”/> W efekcie uzyskujemy nowy typ prosty mogący przybierać wartość zgodną z dowolnym z wymienionych typów. Przykład zastosowania w XML Schema: atrybut maxOccurs 22
Synonimy oraz byty abstrakcyjne Aby dla definicji danego elementu umożliwić tworzenie jego wystąpień pod różnymi nazwami (synonimy lub wersje językowe), stosowane są tzw. grupy substytucji (substitutionGroups). <xsd:element name=”znizka” type=”xsd:decimal” /> <xsd:element name=”upust” substitutionGroup=”znizka” type=”xsd:decimal” /> Elementy służące tylko jako podstawa dla grupy substytucji oraz typy służące wyłącznie jako podstawa dla definicji innych (poprzez rozszerzenie lub ograniczenie), możemy zadeklarować jako abstrakcyjne. Stosujemy w tym celu dla nich atrybut abstract: abstract=”true”. 23
Sposoby budowy schematów - podsumowanie Poprzez zagnieżdżanie: definicje potrzebnych typów tworzone anonimowo na potrzeby poszczególnych deklaracji. Płaski katalog elementów: elementy tworzące hierarchię są deklarowane globalnie, zaś w ich docelowych miejscach występowania w schemacie stosuje się odwołania do tych globalnych deklaracji. Definiowanie typów: globalnie są definiowane nazwane typy, wykorzystywane następnie w deklaracjach elementów i atrybutów. Metoda najskuteczniej ogranicza nadmiarowość, choć wprowadza pewien narzut, wobec czego nadaje się szczególnie dla bardziej rozbudowanych schematów. 24
XPath – nawigacja w strukturze dokumentu 25
XPath - charakterystyka Język deklaratywny służący wskazywaniu elementów, atrybutów, lub całych fragmentów dokumentu XML. Posiada zwięzłą nie-XML-ową składnię, przyjętą w celu umożliwienia umieszczania wyrażeń XPath w wartościach atrybutów oraz w URI. Typy zwracanych wartości: boolean; number (liczba zmiennoprzecinkowa); string; node-set (zbiór węzłów). Ścieżka (ciąg określający) XPath jest zbudowana z tzw. kroków (step), oddzielonych symbolem „/”. Krok może reprezentować: element, atrybut lub funkcję. Poszczególne kroki zawężają obszar przeszukiwania. 26
Kontekst wyrażenia XPath Wyrażenia danego kroku działają w kontekście określanym przez kroki poprzednie. Na kontekst składają się: Bieżący węzeł (tzw. context node); Dwie dodatnie liczby naturalne (pozycja kontekstu - context position oraz rozmiar kontekstu - context size); Wiązania zmiennych; Biblioteka dostępnych funkcji; Zadeklarowane przestrzenie nazwowe widoczne w zakresie wyrażenia. Można podawać alternatywne ścieżki w postaci tzw. wzorca (pattern). Poszczególne ścieżki są wówczas porozdzielane symbolami „|”. 27
Relacje pomiędzy węzłami XPath operuje na drzewie węzłów XML, czyli na strukturze hierarchicznej. Węzłem jest element XML wraz z jego zawartością. Można wyróżnić następujące zależności pomiędzy węzłami: rodzic (parent) – dziecko (child) => pomiędzy elementem nadrzędnym a jego podelementem; rodzeństwo (sibling): poprzednik-następnik => określa kolejność występowania elementów tego samego poziomu; przodek (ancestor) – potomek (descendant): rozszerzenie relacji rodzic-dziecko na zależności pośrednie; 28
Kierunki nawigacji Dostępne są następujące słowa kluczowe - operatory stosowane do selekcji elementów (zwane osiami (?) (axes)): self – aktualny węzeł (zwany kontekstem: context node); parent – rodzic aktualnego węzła; (Tylko dwa powyższe zwracają wyniki nie będące nigdy kolekcjami.) child – bezpośrednie podelementy; descendant – całe drzewo podelementów poniżej aktualnego; descendant-or-self – j.w. plus aktualny węzeł; ancestor – przodkowie aktualnego węzła; ancestor-or-self – j.w. plus aktualny węzeł; following-sibling – kolejne węzły na tym samym poziomie hierarchii; preceding-sibling – wcześniejsze węzły na tym samym poziomie; following – wszystkie węzły zdefniowane za węzłem aktualnym w dokumencie (bez potomków i węzłów atrybutów oraz przestrzeni nazwowych); preceding – wszystkie węzły zdefiniowane przed węzłem aktualnym (bez przodków i węzłów atrybutów oraz przestrzeni nazwowych); 29
Budowa kroku XPath Składnia pojedynczego kroku jest następująca: kierunek (axis); podwójny dwukropek: “::” symbol „*” albo nazwa elementu, albo funkcja: node() text() comment() processing-instruction() <- może zawierać nazwę szukanej instrukcji przetwarzania; dowolna liczba predykatów umieszczonych w nawiasach kwadratowych: „[ ]” Przykłady kroków: child::* child::section[position()=2] ancestor::processing-instruction() 30
Skróty dla typowych kroków XPath Pełna postać Skrót parent::node() .. /descendant-or-self::node() // self::node() . attribute:: @ [position()=2] [2] (Symbol „/” oznacza korzeń dokumentu.) Ponadto child jest kierunkiem domyślnym i wobec tego może być pominięty. Zatem zapisowi: /descendant-or-self::node()/child::para odpowiadać może //child::para a w konsekwencji //para. 31
Wykorzystanie skrótów Uwaga: Globalne wyszukiwania mogą być w swej skróconej postaci nieco mylące. Np. ścieżka /descendant::para[1] zwróci „pierwszy z brzegu” element para, zaś //para[1] zwróci wszystkie elementy para występujące jako pierwsze podelementy swoich rodziców. Ważną rolę odgrywa również skrót “.”, będący odpowiednikiem self::node(). Wraz ze skrótem „//” pozwala na zwięzłe sformułowanie wyrażenia przeszukującego gałęzie począwszy od bieżącego elementu: self::node()/descendant-or-self::node()/child::para odpowiada .//para Podobnie, krok „..” jest skrótem od parent::node(). Np.. ../tytul zastępuje parent::node()/child::title (selekcjonuje potomne elementy tytul). 32
Predykaty wyszukiwania Predykaty zmieniają pozycję kontekstu i rozmiar kontekstu. Występują opcjonalnie, umieszczane wewnątrz nawiasów kwadratowych celem zawężenia zwróconego zbioru elementów. Muszą zwracać wartość logiczną. Dostępne operatory logiczne: = != > >= < <= and or not Dostępne operatory arytmetyczne: + - * div mod Np. //produkt[cena*1.22 < 1000] Jak już wspomniano można wyszukiwać węzły wg ich pozycji, podając predykat np. [position()=2], skracalny do postaci [2] (możliwe także inne porównania, np. „>=”). 33
Operacje stosowalne w predykatach XPath (1) Operacje na łańcuchach tekstowych: concat(łańcuch1, łańcuch2, …) contains(łańcuch, wzorzec) normalize-space(łańcuch) starts-with(łańcuch, wzorzec) string-length(łańcuch) substring(łańcuch, od, do), substring-after(łańcuch, wzorzec), substring-before(łańcuch, wzorzec), translate(łańcuch, stare, nowe), string(…) => konwersja na string; 34
Operacje stosowalne w predykatach XPath (2) Operacje na liczbach: ceiling(liczba), floor(liczba), round(liczba), sum(zbiór_węzłów), number(…) -> konwersja; Operacje na wartościach logicznych: false(), true(), not(); boolean(…) -> konwersja; Operacje na węzłach: count(zbiór_węzłów) last() position() id(identyfikator) -> wyszukanie węzła według indentyfikatora; local-name(element), name(element), namespace-uri(element). 35
Ograniczenia języka XPath Nie można wyszukiwać łańcuchów rozciągających się poprzez kilka elementów; Niemożność wskazania na znaczniki początkowy i końcowy; Niemożność wyszukiwanie encji i sekcji CDATA; Niemożność wskazywania punktów lub zakresów w tekście. Specyfikacja XPointer, przyjmująca nieco inny model dokumentu, uzupełnia te braki. 36