XPath (= XML Path Language) je (dotazovací) jazyk určený převážně ke dvěma věcem:
V současné době standard finišuje již do třetí verze, přičemž obzvláště mezi verzí první a druhou je několikařádový rozdíl ve složitosti (a mocnosti). V tomto úvodu se proto zaměříme především na verzi XPath 1.0.
Princip XPath-dotazu se dá shrnout do následujících čtyř bodů:
Jelikož všechny tyto prvky se dají řetězit za sebou, je kontext z nultého bodu většinou určován výsledkem předchozí části dotazu, případně může být vnucen zvenčí (např. v rámci nějakého nástroje, třeba z DOMu).
Ukažme si to radši rovnou pro ukázkové XML..
..na následujích jednoduchých dotazech:
XPath byl namodelován po vzoru zápisu webových adres a unixové navigaci po adresářové struktuře souborového systému.
Přitom nejčastěji používané navigační prostředky mají přiřazeny zkratky nebo jsou implicitní, aby usnadnily zápisy XPath-výrazů. Například první z ukázkových dotazů..
..by v plné formě měl následující podobu:
Přeloženo to znamená:
Najdi kořenový element, ať se jmenuje jakkoli (/child::*
), a dále pod ním najdi všechny elementy se jménem hlavička (child::hlavička
).
Stejně jako na souborovém systému jsou možné dva způsoby „adresování“ jednotlivých částí stromové reprezentace XML-dokumentu:
/dotaz
);
Navigace po stromu XML-dokumentu se provádí především přímo pomocí jmen elementů a atributů v cestě:
//
tedy nahrazuje libovolně hluboké zanoření do příslušné větve stromu a @
označuje atribut. []
je pak filtrovací predikát.
Druhou možností je použít několik dalších pomocných prvků v podobě XPath-funkcí:
text()
) je totiž jen konkrétní forma obecného uzlu (node()
).
Protože jmenný prostor je součástí jména elementu (či atributu), navigujeme-li po XML-dokumentu obsahujícím jmenné prostory, musíme jména uvádět v jejich příslušné formě:
*
zastupuje libovolný element (bez určení konkrétního jména).
V XPath 1.0 je dohromady následujících třináct os:
osa (zkratka) | význam |
---|---|
ancestor |
předek aktuálního kontextu po větvi stromu směrem ke kořeni |
ancestor-or-self |
jako předchozí + aktuální kontext |
descendant |
libovolný potomek aktuálního kontextu (vrací pouze elementy) |
descendant-or-self (// ) |
jako předchozí + aktuální kontext |
preceding |
vrací všechny listy vyskytující se v dokumentu před aktuálním kontextem (předci aktuálního jsou vynecháni, stejně tak jeho atributy a označení jmenných prostorů) |
preceding-sibling |
jako předchozí, ale týká se pouze listů na stejné úrovni zanoření jako aktuální kontext |
following |
vrací všechny listy vyskytující se v dokumentu po aktuálním kontextu (potomci aktuálního jsou vynecháni, stejně tak jeho atributy a označení jmenných prostorů) |
following-sibling |
jako předchozí, ale týká se pouze listů na stejné úrovni zanoření jako aktuální kontext |
parent (.. ) |
vrací rodiče aktuálního kontextu (pokud existuje) |
self (. ) |
aktuální kontext |
child |
přímý potomek aktuálního kontextu |
attribute (@ ) |
atribut aktuálního elementu |
namespace |
vrací označení jmenných prostorů pro aktuální element |
Osy jsou navrženy tak, že ancestor, descendant, following, preceding a self se navzájem nepřekrývají a dohromady složí celý dokument:
Většina dotazů na cestu v XPath'u skončí s více než jedním vyhovujícím uzlem stromu, a to je právě místo, kde přijdou na řadu predikáty – aniž by změnily aktuální kontext, vyberou z návratové množiny pouze tu podmnožinu prvků, která vyhovuje nějakému dodatečnému požadavku.
Predikáty se píší do hranatých závorek []
. Několik jsme jich už viděli v předchozích příkladech – například test na přítomnost atributu ([@lang]
) nebo test na konkrétní hodnotu atributu ([@lang='cze']
). Dodatečné požadavky však mohou být mnohem složitější, jak ukazuje tento krásný příklad z Wikipedie:
Výše uvedený predikát vrací hodnoty atributu @target ze všech těch elementů <a>, které postupně splňují následující podmínky:
[/html/@lang='en'] |
Nachází se v dokumentu, na jehož kořenovém elementu html je nadefinován atribut lang s hodnotou en. (V rámci predikátu tedy můžeme beztrestně změnit kontext vyhodnocované podmínky.) |
[@href='help.php'] |
Jejich atribut href obsahuje hodnotu help.php. (Tady už jsme zase zpátky v kontextu zpracovávaného elementu a.) |
[1] |
Z vyhovující podmnožiny elementů a se vybere pouze první v pořadí podle výskytu ve zdrojovém dokumentu. |
Na jednom cvičení se studentům podařilo vymyslet následující obzvláště vychytaný predikát..
..s následujícím významem:
//* |
Vyber z dokumentu všechny elementy.. |
[@*] |
..které mají alespoň jeden atribut.. |
[starts-with(name(.),'č')] |
..jehož jméno začíná na písmeno č. |
*
zastupuje nejdříve element libovolného jména a následně ještě atribut libovolného jména.
Použití nejen predikátů výrazně zvyšují operátory a funkce, které jsou v rámci XPath'u k dispozici. Některé jsme již také viděli v akci – operátor =
pro srovnání hodnot, funkci not()
pro negaci výsledku výrazu, funkci count()
pro zjištění počtu uzlů vyhovujících danému dotazu.
Zatímco obor působnosti funkcí je vcelku jasně určen typy vstupů (případně i výstupů), tak u operátorů podobně jasné rozdělení neexistuje – podle druhu a kontextu mohou operovat nad různými datovými typy a s různým efektem.
string concat(STR, STR, STR*)
boolean contains(V_ČEM, CO)
string normalize-space(STR?)
string substring(STR, ODKUD, JAK_DALEKO?)
, string substring-before(STR, PODŘETĚZEC)
, string substring-after(STR, PODŘETĚZEC)
boolean starts-with(CO, ČÍM)
number string-length(STR?)
string translate(STR, CO, ČÍM)
number sum(NODESET)
number round(NUM)
, number floor(NUM)
, number ceiling(NUM)
number count(NODESET)
string name(NODESET?)
string local-name(NODESET?)
, string namespace-uri(NODESET?)
number last()
number position()
node-set id(OBJEKT)
Pravdivostní hodnota typu true/false se v XPath přímo nevyskytuje, místo ní se ve výrazech používají příslušné funkce:
boolean true()
, boolean false()
Další pravdivostní funkce:
boolean not(BOOLEAN)
boolean lang(LANG)
boolean boolean(OBJEKT)
number number(OBJEKT?)
string string(OBJEKT?)
Operátory jsou kapku složitější – podle druhu a kontextu mohou operovat nad různými datovými typy. A tak jsou některé z nich funkční:
+
, zbytek po dělení mod
…); |
, libovolně zanořený potomek //
…); =
, nerovnost !=
…). Téměř všechny operátory, osy, funkce a identifikátory z této skupiny jsme již viděli v příkladech. Patří sem:
/
, //
[…]
*
node()
, text()
, comment()
, processing-instruction()
|
.
..
@
PS: Z důvodu přehlednosti a úplnosti jsem zde pod označení „operátor“ zahrnul všechny prvky používané v XPath 1.0 pro identifikaci cesty.
Zatímco porovnávacích operátorů je standardní sada..
=
!=
<
>
<=
>=
..a s výjimkou porovnávání množin uzlů (viz následující slajd) se chovají standardně, pro logické operace je k dispozici klasické..
and
or
..a negaci zajišťuje nikoli operátor, ale funkce not().
Pořadí priorit operátorů od nejvyšší po nejnižší je následující:
<= < >= > |
= != |
and |
or |
Přitom jsou všechny operátory zleva asociativní (takže platí např. ekvivalence 3>2>1 ⇔ (3>2)>1 ⇔ false
).
Je-li třeba použít operátory <
nebo <=
uvnitř XML, musíme je pochopitelně odiskejpovat, například:
V porovnávání s množinou uzlů se skrývá jedna drobná „zrada“ – porovnáváme-li (skalární) hodnotu s množinou uzlů, porovnává se tato hodnota postupně až se všemi hodnotami z množiny uzlů a výsledek je existenční, tj. stačí jediná hodnota pro rozhodnutí o výsledku. Přímým důsledkem je, že například neplatí ekvivalence mezi not(A=B)
a A!=B
:
Snad ještě více matoucí je toto srovnání pro dvě množiny uzlů, protože také probíhá přes všechny možné dvojice, a tak kupříkladu platí zároveň jak..
'foo' = 'foo'
) ..tak i:
'foo' != 'bar'
) Dále uvedené operátory operují buď přímo nad čísly nebo na uzly, jejichž obsah je na číslo automaticky převeden (podle pravidel funkce number()). To znamená, že jejich argumenty mohou být přímo XPath-výrazy vedoucí na konkrétní uzel!
Sčítání, odčítání a násobení zajišťují:
+
-
*
-
. Tento znak je totiž povolen ve jménech elementů i atributů, proto je rozdíl mezi foo-bar a foo - bar!
*
, který u výběru uzlů zastupuje elementy nebo atributy.
Pro dělení je pak k dispozici:
div
mod
Vizualizaci xpathových výrazů zvládnou prakticky všechny profesionální XML-editory plus několik specializovaných nástrojů. A pokud vám zrovna stačí něco odlehčenějšího, zkuste třeba webový vizualizátor Chrise Nielsena.
Nemáte-li některý z nich k dispozici, vždy si ještě můžete napsat příslušný program v XSLT, pustit ho pod vhodným překladačem a podívat se na výsledek.