XQuery jest językiem służącym do „odpytywania” dokumentów XML, wydobywania informacji z dokumentów XML. W swoich założeniach ma być tym dla XML, czym SQL jest dla relacyjnych baz danych.
Język XQuery zdefiniowany jest w rekomendacji:
Standard uzupełniają inne dokumenty:
Standard XQuery jest silnie związany z XPath. Oba opierają się o ten sam model danych i posiadają tą samą bibliotekę funkcji (i operatorów).
O samym języku XQuery można myśleć jak o rozszerzeniu języka XPath, jednak definicje obu tych języków są osobnymi rekomendacjami.
O ile wyrażenia XPath są wykorzystywane w innych standardach (XSLT, XPointer, XML Schema), to XQuery jest samodzielnym standardem. Jednak, w przeciwieństwie do wielu innych standardów W3C :), zapytanie XQuery nie jest dokumentem XML.
Na potrzeby naszych zajęć (bawimy się na plikach a nie bazach danych) wystarczy jedno z poniższych narzędzi.
Podczas ćwiczeń w labie najwygodniej korzystać z XML Spy pod Windows.
Należy napisać lub otworzyć zapytanie XQuery. W menu należy wybrać XSL/XQuery > Execute XQuery. Można wskazać dokument kontekstowy lub nie. W zależności od tego zwykłe ścieżki XPath będą działać lub nie.
Saxon to procesor XSLT i XQuery dla Javy i .NET. Obsługuje standardy XPath 2.0, XSLT 2.0, XQuery 1.0. Udostępnia zarówno prosty interfejs command-line, jaki i API (dla Javy zgodne z JAXP), dzięki któremu z procesorów można korzystać we własnych programach.
Oprogramowanie w podstawowej wersji "Home Edition" jest darmowe (open-source). Wersja zaawansowana (ze wsparciem dla przetwarzania schema aware) jest komercyjna.
W ścieżce klas powinien występować plik saxon9.jar. Przykłady użycia interfejsu command-line:
-cp
.2+2
.Wartosc
.
Jeśli napis zawiera spacje lub podejrzane znaki, najlepiej ująć go w apostrofy.(Prawie?) każde wyrażenie XPath jest poprawnym wyrażeniem XQuery. A pojedyncze wyrażenie jest poprawnym zapytaniem.
Poprawnymi zapytaniami są więc wyrażenia arytmetyczne, logiczne, wyrażenia na sekwencjach itp.
Zapytaniami XQuery są także ścieżki XPath (i wyrażenia zawierające ścieżki). Aby jednak ścieżka bezwzględna mogła zostać obliczona, musi być znany dokument kontekstowy, a dla ścieżki względnej także węzeł kontekstowy w tym dokumencie.
Za pomocą funkcji doc
można odczytywać zawartość dokumentów XML z zewnętrznych źródeł danych
(plików, adresów URL). Np. doc("przyklad.xml")
zwraca węzeł dokumentu zapisanego w pliku przyklad.xml.
Dzięki temu poprawnymi zapytaniami są ścieżki takie jak doc("przyklad.xml")/lista/obiekt[@id='E4]
.
Zapytania takie działają niezależnie od dokumentu kontekstowego (nie trzeba go określać).
Przed ciałem zapytania dokument XQuery może zawierać deklaracje. Oto niektóre z nich:
xquery version "1.0" encoding "utf-8"; declare namespace foo = "http://example.org"; declare default element namespace "http://inny.org"; <foo:bar> Coś tu <xx> Coś tam </xx></foo:bar>
xquery version "1.0" encoding "utf-8"; declare variable $id as xs:string external; declare variable $doc := doc("przyklad.xml"); $doc//obiekt[@id = $id]
Wynik zapytania XQuery można zapisać ("zserializować") na różne sposoby, m.in jako XML i jako tekst.
Do wyboru sposobu serializacji służą parametry serializacji, z których najważniejszym jest method
.
Domyślnie wynik wypisywany jest w postaci XML. Dokładie mówiąc wynikiem takiej serializacji nie musi być dokument XML, a tzw. encja ogólna przetwarzana (general parsed entity), czyli wynik przekształcenia wstawiony do sztucznego elementu głównego utworzy poprawny dokument XML.
Spróbuj wykonać poniższe zapytania wybierając metody serializacji xml
i text
.
W XML Spy ustawia się to w menu XSL/XQuery > XSL/XQuery Settings:
2 + 2
(1, "Ala", 3.0)
doc("przyklad.xml")/lista/obiekt[@id='E7']
doc("przyklad.xml")/lista/obiekt[@nazwa]
doc("przyklad.xml")//text()
doc("przyklad.xml")//*
Dla dokumentu pracownicy.xml napisz zapytania zwracające:
Wyrażeniem XQuery może być nie tylko odpowiednik wyrażenia XPath, ale także konstruktor. Dzięki temu w wynikowej sekwencji mogą pojawić się nie tylko węzły odczytane z dokumentów, ale także nowe, dynamicznie skonstruowane.
Konstruktory dzielą się na bezpośrednie (exact) i obliczane (computed).
Jeżeli w zapytaniu XQuery znajdzie się znacznik otwierający elementu, to cały fragment zapytania od tego znacznika aż do odpowiadającego mu znacznika zamykającego zostanie potraktowany jak konstruktor. W wyniku zapytania znajdzie się właśnie ten element. Atrybuty i zawartość tekstowa wewnątrz także zostaną po prostu skopiowane do wyniku.
Bezpośredni konstruktor elementu book
.
<book isbn="isbn-0060229357"> <title>Harold and the Purple Crayon</title> <author> <first>Crockett</first> <last>Johnson</last> <?cel Wartość?> <!--Komentarz--> </author> </book>
Bezpośredni konstruktor elementu umieszczony wewnątrz wyrażenia.
for $ob in doc("przyklad.xml")//* return <elem>Element o nieustalonej nazwie</elem>
Jeżeli wewnątrz wygenerowanego w ten sposób elementu chcemy umieścić wartość obliczoną dynamicznie, możemy umieścić wyrażenie XQuery w nawiasach klamrowych.
Zafgnieżdżone wyrażenie (jak każde) oblicza się do jakiejś sekwencji. W miejsce wystąpienia wyrażenia
wstawiana jest wynikowa sekwencja, z tym że wartości atomowe są rzutowane na xs:string
,
a sąsiadujące wartości atomowe są sklejane do pojedynczych węzłów tekstowych, z pojedynczymi spacjami pomiędzy.
Węzły są wstawiane jako węzły.
Wyrażenie w nawiasach klamrowych można umieścić też w wartości atrybutu. W tym przypadku
wynik zagnieżdżonego wyrażenia (łącznie z węzłami) jest rzutowany na xs:string
(między elementami sekwencji wstawiane są pojedyncze spacje).
Bezpośredni konstruktor elementu elem
jest umieszczony w wyrazeżeniu,
a sam zawiera wyrażenia obliczające wartość atrybutu i część zawartości elementu.
<wynik>{ for $el in doc("przyklad.xml")//* return <elem głębokość="{count($el/ancestor::node())}">Element o nazwie: {name($el)}</elem> }</wynik>
Konstruktory obliczane są bardziej ogólnym sposobem tworzenia węzłów XML. W szczególności pozwalają one na dynamiczne generowanie nazw elementów czy atrybutów.
Konstruktor obliczany elementu book
(ze statyczną zawartością).
element book { attribute isbn {"isbn-0060229357" }, element {"title"} { "Harold and the Purple Crayon"}, element author { element first { text { "Crockett" } }, element last {"Johnson" } processing-instruction cel { "Wartość" } commment { "Komentarz" } } }
Konstruktor obliczany, w którym obliczana jest nie tylko zawartość, ale także nazwa elementu.
<wynik>{ for $el in doc("przyklad.xml")//* return element {concat('elem-', name($el))} { attribute głębokość {count($el/ancestor::node())}, text {"Element o nazwie: "}, text {name($el)} } }</wynik>
Sprawdź, czy bezpośrednimi konstruktorami są komentarz, instrukcja przetwarzania i fragment tekstu (umieszczone po prostu w wyrażeniu XQuery).
Wyrażenia for
z XPath, w XQuery są zastąpione przez bardziej rozbudowane wyrażenia FLWOR
(od For, Let, Where, Order by, Return).
Jest to odpowiednik wyrażeń SELECT
z SQL.
Wyrażenie składa się z następujących części (klauzul):
for
– wiąże zmienną, jej wartość przebiega wszystkie elementy sekwencji,let
– wiąże zmienną jednorazowo przypisując jej wartość (całą sekwencję),where
– filtruje elementy sekwencji,order by
– sortuje sekwencję,return
– wyrażenie obliczane dla każdego elementu sekwencji (tej z for
ale przefiltrowanej i posortowanej).
Klauzule for
i let
mogą występować wielokrotnie i być wymieszane. where
i order
są opcjonalne.
let $obiekty := doc("przyklad.xml")/lista/obiekt return <wynik>{ $obiekty }</wynik>
for $obiekt in doc("przyklad.xml")/lista/obiekt return <wynik>{ $obiekt }</wynik>
for $obiekt in doc("przyklad.xml")/lista/obiekt let $pop := $obiekt/preceding-sibling::element() let $nazwa-pop1 := $pop[1]/@nazwa where $obiekt/@nazwa order by $obiekt/@nazwa return <wynik> Obiekt o nazwie {xs:string($obiekt/@nazwa)} ma {count($pop)} poprzedników. Najbliższym poprzednikiem jest obiekt o nazwie {xs:string($nazwa-pop1)}. </wynik>
Weź plik sklep1.xml
Napisz zapytanie, które wypisuje kolejno wszystkie kategorie i dla każdej kategorii wszystkie towary z tej kategorii.
Spróbuj użyć elementów HTML do sformatowania wyniku (prawdopodobnie w XML Spy można to wtedy wyświetlić jeśli użyje się serializacji do (X)HTML).
To samo, ale dla pliku sklep2.xml. Tak, żeby kategorie się nie powtarzały!
Dla dokumentu pracownicy.xml napisz zapytania zwracające dla każdego działu:
W XQuery istnieje możliwość definiowania własnych funkcji, których następnie można używać w wyrażeniach (także w innych funkcjach). Funkcje powinny być zadeklarowane w prologu dokumentu XQuery (przed ciałem zapytania, obok innych deklaracji).
Własne funkcje powinno się definiować we własnych przestrzeniach nazw lub w predefiniowanej przestrzeni nazw o prefiksie local
.
declare function local:podwoj( $x as xs:double) as xs:double? { 2* $x }; (: Filtruje wartosci z sekwencji przepuszczając tylko mniejsze od zadanej wartości :) declare function local:tylkoMniejsze( $lista, $wartosc as xs:decimal) { for $el in $lista where $el < $wartosc return $el }; <wynik> <pierwszy> { local:podwoj(100) } </pierwszy> <drugi> { let $seq := (1, 20, 3, 40, 5, 60) return local:tylkoMniejsze($seq, 10) } </drugi> </wynik>
Dalej pliki ze sklepem. Zdefiniuj funkcję średnia-cena
obliczającą średnią cenę w podanej
sekwencji towarów (lub średnią cenę w podanej kategorii).
Używając funkcji napisz zapytanie, które filtruje sklep2.xml zachowując strukturę dokumentu i kolejność elementów towar
, ale usuwając towary droższe niż średnia dla danej kategorii.