XML – ćwiczenia 7: XPath i XQuery

Standardy

Standardy służące przetwarzaniu dokumentów XML: XSLT, XPath i XQuery, są ze sobą związane i występują ogólnie rzecz biorąc w dwóch wersjach.

  1. Starsza i prostsza:

  2. Nowsza i bogatsza:

Technikalia

Proponuję korzystać z narzędzia Saxon w wersji 9.x HE (darmowa, Open-Source).

Należy pobrać plik saxon9he.jar. Następnie można uruchamiać Saxona m.in. w poniższy sposób:

Wyrażenia XPath

Zobacz slajdy z wykładu.

Zadanie 1.

Za pomocą XPath wybierz określone elementy dokumentu przyklad.xml lub wyraź określone konstrukcje.

  1. wszystkie obiekty,
  2. zagnieżdżone opisy (tzn. znajdujące się w innych opisach),
  3. obiekt o id równym E4,
  4. obiekty z opisem,
  5. obiekty bez opisu,
  6. opisy w obiektach bez nazwy,
  7. tylko drugie opisy obiektów,
  8. obiekty parzyste,
  9. dodatki znajdujące się za opisami,
  10. dodatki znajdujące się bezpośrednio za opisami,
  11. obiekty, których wszystkie dzieci posiadają podelement wyr,
  12. sekwencja: dla każdego obiektu, liczba jego dzieci.

Zobacz także

XQuery

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.

Związek z XPath

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.

Składnia - zobacz slajdy z wykładu. Poniżej dodatkowe przykłady.

Deklaracje

Przykład 1. Przykłady deklaracji

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]

Sposoby serializacji

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.

Zadanie 2.

Dla dokumentu pracownicy.xml napisz zapytania zwracające:

  • liczbę wszystkich pracowników
  • liczbę pracowników księgowości
  • listę wszystkich pracowników księgowości (elementów XML)

Użyj równych sposobów serializacji wyniku.


Write and execute some XQuery queries on staff.xml

Konstruktory węzłów XML

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).

Przykład 2.

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).

Przykład 3.

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>

Przykład 4.

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>

Zadanie 3.

Sprawdź, czy bezpośrednimi konstruktorami są komentarz, instrukcja przetwarzania i fragment tekstu (umieszczone po prostu w wyrażeniu XQuery).

Wyrażenia FLWOR

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):

Klauzule for i let mogą występować wielokrotnie i być wymieszane. where i order są opcjonalne.

Przykład 5. Przykłady wyrażeń FLWOR

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>

Zadanie 4.

Weź plik sklep1.xml

Napisz zapytanie, które wypisuje kolejno wszystkie kategorie i dla każdej kategorii wszystkie towary z tej kategorii.

Użyj elementów HTML do sformatowania wyniku.


A different story, but technically requires the same approach...

Take staff example. For each department, print the department name, the number of employees in that department, and then print all employees' names.

Print results as HTML, for example print each department name as h2 and the list of employees as ul.

Zadanie 5.

To samo, ale dla pliku sklep2.xml. Tak, żeby kategorie się nie powtarzały!


For each position ("job", in other words), print the number of employees hired with that position in the whole company and then print all employees' names.

Avoid duplications:

Definiowanie własnych funkcji

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.

Przykład 6.

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>

Zadanie 6.

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.


Write an XQuery function counting the average salary for a given position (or a given sequence of person elements, if you prefer).

Use it to count average salary for each position.

Optionally: filter employees printing only those, who earn less than the average for his/her position.

Zobacz także


Valid XHTML 1.1Valid CSS