XPath – rychle to najdeme

V dnešním pokračování seriálu o práci s dokumenty ve formátu XML v jazyce PHP se podíváme na dotazovací jazyk XPath. Na rozdíl od předcházejících metod zpracování XML v XPath neurčujeme, jak se má s informacemi naložit, ale specifikujeme, jaká data chceme.
Seriál: Přehled podpory XML v PHP5 (6 dílů)
- Přehled podpory XML v PHP5 5. 10. 2009
- PHP a XML: SAX – čteme pěkně popořádku 12. 10. 2009
- XMLReader – když se zamotáme do SAX 19. 10. 2009
- DOM – načteme to do paměti 26. 10. 2009
- XPath – rychle to najdeme 2. 11. 2009
- XSLT – jazyk budoucnosti 9. 11. 2009
Nálepky:
Všechny předchozí příklady načítání dokumentu XML měly jednu věc společnou – v kódu bylo přesně vyjádřeno (byť různě úsporným způsobem), co přesně má program s dokumentem XML dělat, aby získal potřebné informace. Vývoj programování však spěje k tomu, aby se tento procedurální přístup používal co nejméně, a místo toho jsou stále oblíbenější deklarativní přístupy, kdy pouze specifikujeme, co chceme získat, a počítač se o to už nějak postará. Klasickým příkladem takového jazyka je dotazovací jazyk SQL, který umožňuje jednoduše a stručně formulovat požadavek na získání nějakých dat z relační databáze. Při použití SQL jsme přitom zcela odstíněni od toho, jak se k výsledku dotazu konkrétně dojde. Podobnou roli ve světě XML plní dotazovací jazyk XPath, ten umožňuje v jednoduché syntaxi zapsat dotaz, který vybere určité uzly nebo hodnotu z dokumentu XML.
Jazyk XPath přitom jako mnoho jiných technologií operuje nad stromovou reprezentací dokumentu. Abychom mohli v PHP pokládat nad dokumentem XML dotazy, musíme jej nejprve načíst do paměti do klasického DOM stromu.
$doc = new DomDocument(); $doc->load("dokument.xml");
Nad DOM stromem si pak můžeme vytvořit objekt, který dovoluje provádění dotazů v jazyce XPath:
$xpath = new DOMXPath($doc);
Ve své nejjednodušší podobě umožňuje XPath zapisovat dotazy, které jsou podobné cestě k nějakému souboru na disku. Nepohybujeme se však v adresářové struktuře, ale po stromové struktuře dokumentu XML. Například dotaz /rss/channel/title
vrátí uzel odpovídající elementu title
, který je uvnitř elementu channel
, který je uvnitř elementu rss
. Dotaz /rss/channel/item
pak vybere elementy item
, které jsou uvnitř elementu channel
, který je uvnitř elementu rss
.
Chcete se naučit o PHP víc?
Akademie Root.cz pořádá školení Kurz programování v PHP5. Jednodenní kurz programování v PHP 5 je určen všem webovým vývojářům, kteří se chtějí do hloubky seznámit a sžít s programovacím jazykem PHP ve verzi 5. První část kurzu je zaměřena na nový objektový model se všemi jeho vlastnostmi, ošetření chyb pomocí výjimek a efektivní využití těchto konceptů. Druhá část je zaměřena na nové knihovny PHP 5, především pro práci s databázemi, XML a objekty. Pozornost je věnována i zajištění kompatibility s PHP 4, přechodu z této verze a výhledu na PHP 6. Máte zájem o jiné školení? Napište nám!
XPath dotaz můžeme vyhodnotit pomocí metody evaluate()
:
$vysledek = $xpath->evaluate("/rss/channel/title");
Je-li výsledkem dotazu seznam uzlů, je tento seznam vrácen jako instance třídy DOMNodeList
. V případě, že nás zajímá jen textový obsah elementu, můžeme v XPathu použít funkci string()
– například string(/rss/channel/link)
. Ve skriptu tak název kanálu můžeme velice jednoduše vypsat pomocí příkazu:
echo $xpath->evaluate("string(/rss/channel/title)");
Použití XPathu pro vypsání informací z dokumentu RSS ukazuje následující příklad.
Příklad 6. Dotazování pomocí XPathu – xpath.php
<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN'> <html lang="cs"> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8"> <title>Přehled zpráv</title> </head> <body> <?php // vytvoření DOM stromu ze souboru $doc = new DomDocument(); $doc->load("../data/luparss.xml"); // vytvoření objektu pro vyhodnocování dotazů XPath $xpath = new DOMXPath($doc); ?> <h1>Přehled aktuálních zpráv ze serveru <a href="<?php echo htmlspecialchars($xpath->evaluate("string(/rss/channel/link)"), ENT_QUOTES)?>"><?php echo htmlspecialchars($xpath->evaluate("string(/rss/channel/title)"))?></a> </h1> <dl> <?php // výběr všech položek v kanálu $polozky = $xpath->evaluate("/rss/channel/item"); // postupné zpracování všech položek foreach($polozky as $polozka) { echo "<dt><a href='" . htmlspecialchars($xpath->evaluate("string(link)", $polozka), ENT_QUOTES) . "'>" . htmlspecialchars(($xpath->evaluate("string(title)", $polozka))) . "</a></dt>n"; echo "<dd>" . htmlspecialchars(($xpath->evaluate("string(description)", $polozka))) . "</dd>n"; } ?> </dl> </body> </html>
Ukázkový příklad je s využitím XPathu velmi jednoduchý. Využívá ještě jednu zajímavou možnost metody evaluate()
. Zadáme-li jako druhý parametr nějaký uzel ve stromu dokumentu XML, vyhodnotí se dotaz relativně vzhledem k tomuto uzlu. Dotaz link
proto vybere podelement link
u aktuálně zpracovávané položky (element item
). Zápisem string(link)
získáme textový obsah elementu link
.
V příštím díle se podíváme na transformační jazyk XSLT, který v mnoha případech nabízí zdaleka nejefektivnější možnosti pro manipulaci s dokumenty XML.
Více informací o knize naleznete na stránkách nadavatelství Grada a na stránkách autora.
Ještě bych doplnil: pokud si přejeme získat všechny elementy „title“ bez ohledu na to, kde jsou ve stromu zasazeny, můžeme použít:
$vysledek = $xpath->evaluate(„//title“);
Tedy na začítek nasadíme dvě lomítka. (Alespoň takhle jsem to dřív dělal já :)
… ale přesně TOHLE je určitě popsáno v knize, že? (nerad bych knihu sabotoval..)
Já bych zase dopnil, že // není dobré používat, pokud to není nezbytně nutné, protože naivní implementace XPathu (ta je třeba i v PHP) procházejí všechny uzlu dokumentu a vyhodnocení dotazu je tak relativně pomalé. Je lepší používat co nejvíce specifické dotazy.
Zajímalo by mne, jaka je paměťová náročnost čtení přes DOM, případně použití XPathu. Nevíte někdo ?
Hmm, nekdy mala, nekdy velka, nekdy tak akorat… :-)
Vazne, zalezi na tom jaky zpracovavate dokument a jake jsou pozadavky Vasi aplikace. Existuji ale i implementace (ne v PHP) XPath bez nacitani dokumentu do pameti, napr. v ramci http://exist.sourceforge.net/.
Záleží na konkrétní implementaci a konkrétním dokumentu XML, ale obvykle v paměti DOM strom zabere 3–10× více prostoru než stejný dokument při serializaci do XML.
XPath samotný paměťově náročný není je to dotazovací jazyk – podstatné je, nad jakým úložištěm dotaz pokládáte. V PHP můžete XPath pokládat nad DOM nebo SimpleXML reprezentací (v paměti zaberou v podstatě stejně místa). Pokud je dat hodně a do paměti se nevejdou, je vhodné pro ukládání použít nějakou databázi XML a nad ní spouštět XPath/XQuery dotazy.
Podmnožinu XPathu lze navíc vyhodnocovat streamovaně a zpracovávat tak dokumenty teoreticky neomezené velikosti. Umí to napřípad STXPath (http://stx.sourceforge.net/), bude to umět připravované XSLT 2.1.
Díky za odpověď.