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

<?xml version="1.0" encoding="utf-8" ?>
<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

<?xml version="1.0" encoding="utf-8" ?>
<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





Читайте также:



8 Ответов на “PHP+XML+XPath. Часть 2”

  1. alexey

    добрый день, статейка реальная.. это точно.. но почему-то не работает test.php из архива.. выдает что-то такое:

    Fatal error: Call to undefined function xmldocfile() in Y:\home\creamax\www\xquery\class_xquery_lite.php on line 163

    в чом может быть проблемма?

  2. novice

    У Вас не установлено расширение DOM XML

  3. Grave D.

    а если оно установлено, а ошибка всё же присутсвует?

    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

  4. novice

    xmldocfile - это устаревшая (deprecated) функция. Скорее всего поэтому ее не видно. Попробуйте использовать ее аналог - domxml_open_file

  5. Grave D.

    Пробовал и domxml_open_file - та же ошибка.

  6. Grave D.

    О! Нашел кое что интересное!)))
    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/
    :)

  7. Grave D.

    да, вместо $url ‘test.xml’ нужно вбить:)

  8. Andy

    Хотелось бы заметить, что xpath() поддерживает namespaces, что может сыграть злую шутку с рядом пользователей. То есть она может не находить элементарные запросы, которые кажутся элементарными. Например, xpath(‘./street’), xpath(‘//address’). Если эти пути находятся позже элемента имеющего тот или иной атрибут xmlns=”…”.

    способы лечения php.net/manual/ru/simplexmlelement.xpath.php

    и

    asXML());
    ?>

    В последнем случае objImageXml это выборка элементов после элемента с атрибутом xmlns=”…”

    Весь день провозился пока понял, что проблема в поддержке namespaces.


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map