PHP+XML+XPath. Часть 2
Продолжаем тему XPath, начатую в статье PHP+XML+XPath. Часть 1. В конце предыдущей статьи я обещал рассмотреть оси в XPath и математические функции. |
Ось child
Когда мы пишем запрос вида /books/book, мы на самом деле подразумеваем /child::books/child::book, или /child::books/book, или /books/child:book. Любой из этих вариантов имеет один и тот же смысл: взять дочерние элементы контекстного узла. По умолчанию child:: можно опустить, чтобы не загружать мозги лишними символами
Заметим еще, что имя оси, т.е. «child» относится к элементу, после которого пишется. Т.е. в запросе /books/child:book ключевое слово child относится к books.
Оси descendant и descendant-or-self
Если у узлов есть «дети», то должны быть и «потомки». Данная ось как раз и подразумевает это:
/descendant::* - выберет все потомки корневого узла (все элементы), не включая сам контекстный узел
/books/book/descendant::* - выберет все потомки узла book, у которого предок books
Например:
test.xml
<books>
<book>
<title>Название книги</title>
<author>
<firstname>Имя</firstname>
<lastname>Фамилия</lastname>
</author>
<pages>Количество страниц</pages>
<isbn>ISBN</isbn>
<year>Год издания</year>
</book>
</books>
Для этого случая запрос /books/book/descendant::* выберет следующие узлы:
title, author, firstname, lastname, pages, isbn, year
Или тот же самый результат мог бы дать запрос //book/descendant::*
Запрос //book/descendant::test даст все узлы test, содержащиеся в узле book. Но это не то же самое, что и //book/test ! Разницу покажу на примере:
test2.xml
<books>
<book>
<title>Название книги</title>
<test>Test1</test>
<author>
<test>Test2</test>
<firstname>Имя</firstname>
<lastname>Фамилия</lastname>
</author>
<pages>Количество страниц</pages>
<isbn>ISBN</isbn>
<year>Год издания</year>
</book>
</books>
Запрос //book/test выбрал бы один элемент с текстом «Test1». А запрос //book/descendant::test выбрал бы два элемента (включая «Test2»).
Ось descendant-or-self включила бы и сам контекстный узел в результат запроса: //book/descendant::test выбрал бы три элемента: два элемента test и один book.
Ось parent
Допустим мы хотим выбрать все узлы, которые являются родителями для заданного узла. Например, выберем всех родителей узла test: //test/parent::*
Таковыми в случае test2.xml будут book и author.
Оси ancestor и ancestor-or-self
Эта ось используется для выбора предков указанного узла. Например: //test/ancestor::* - выберет всех предков узла test: books, book, author. Тут еще можно заметить, что эта ось всегда выберет корневой элемент (books), если контекстный узел (в нашем случае - test) сам не корневой элемент.
А если мы хотим включить в результат запроса и сам контекстный узел, то используется ось ancestor-or-self. В этом случае ось всегда выберет корневой элемент.
Оси following-sibling и preceding-sibling
Другими словами, «оси братьев». Ось following-sibling содержит всех следующих братьев контекстного узла. Например, запрос //author/following-sibling::* выберет узлы pages, isbn, year, т.к. они являются «братьями» (или «сестрами», если хотите ) по отношению к узлу author (имеют одного родителя).
Ось preceding-sibling, напротив, содержит всех предыдущих братьев узла: //author/ preceding-sibling::* выберет узлы test и title.
Оси following и preceding
Ось following нужна для выбора всех узлов, идущих после контекстного, в том порядке, в котором они определены в документе: /books/book/author/test/following::* выберет firstname, lastname, pages, isbn, year.
Ось preceding - наоборот, для выбора всех узлов, идущих перед контекстным: /books/book/author/test/preceding::* выберет books, book, title, test, author.
Математические функции и операторы
XPath поддерживает несколько простых математических функций и операторов:
оператор div делит одно число на другое (5 div 2 будет 2)
оператор mod возвращает остаток от деления (5 div 2 будет 1)
функция floor округляет до целого в меньшую сторону (floor(5.3) будет 5)
функция ceiling округляет до целого в большую сторону (floor(5.3) будет 6)
функция round просто округляет число (round(5.5) будет 6)
Зачем они тут нужны? Ну а вдруг Вам нужно будет вычислить позицию узла по определенным правилам. Например, выбрать все узлы на четных позициях:
//*[position() mod 2 = 0 ]
Или выбрать все элементы, наиболее расположенные к центру:
//*[ position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5) ]
Итоги
Основные моменты в XPath нами рассмотрены. Остальные моменты можно найти в официальной документации по XPath. Теперь мы можем смело парсить xml-файлы с помощью мощного языка запросов. Но XPath - это еще не все. Есть еще XQuery - более удобный язык задания запросов к XML, похож на SQL и … ладно, не буду забегать вперед - всему свое время
P.S. Другие статьи на тему парсинга xml:
Учимся парсить XML на PHP
Парсим легко и просто с помощью SimpleXML
Введение в XPath
добрый день, статейка реальная.. это точно.. но почему-то не работает test.php из архива.. выдает что-то такое:
Fatal error: Call to undefined function xmldocfile() in Y:\home\creamax\www\xquery\class_xquery_lite.php on line 163
в чом может быть проблемма?
У Вас не установлено расширение DOM XML
а если оно установлено, а ошибка всё же присутсвует?
phpinfo();
domDOM/XML enabled
DOM/XML API Version 20031129
libxml Version 2.6.32
HTML Support enabled
XPath Support enabled
XPointer Support enabled
Schema Support enabled
RelaxNG Support enabled
xmldocfile - это устаревшая (deprecated) функция. Скорее всего поэтому ее не видно. Попробуйте использовать ее аналог - domxml_open_file
Пробовал и domxml_open_file - та же ошибка.
О! Нашел кое что интересное!)))
PHP5 не поддерживет XQuery зато он поддерживает XPath 1.0!
Код для вашего примера!
$xmlDocument = new DOMDocument();
if ($xmlDocument->load($url)) {
$xpath = new DOMXPath($xmlDocument);
$query = ‘//bib/book[@year > 1991]';
$nodeList = $xpath->query($query, $xmlDocument);
header(‘Content-Type: text/plain’);
foreach ($nodeList as $node) {
echo $xmlDocument->saveXML($node) . “\r\n”;
}
}
Красиво и никаких лишних классов! Пользуйтесь:)
зы: Логичнее было бы перенести эту обсуждение вот сюда https://i-novice.net/poisk-v-xml-s-pomoshhyu-xquery/
да, вместо $url ‘test.xml’ нужно вбить:)
Хотелось бы заметить, что xpath() поддерживает namespaces, что может сыграть злую шутку с рядом пользователей. То есть она может не находить элементарные запросы, которые кажутся элементарными. Например, xpath(‘./street’), xpath(‘//address’). Если эти пути находятся позже элемента имеющего тот или иной атрибут xmlns=”…”.
способы лечения php.net/manual/ru/simplexmlelement.xpath.php
и
asXML());
?>
В последнем случае objImageXml это выборка элементов после элемента с атрибутом xmlns=”…”
Весь день провозился пока понял, что проблема в поддержке namespaces.