PHP+XML+XPath. Часть 1

Вот я и добрался до XPath, как и обещал. Изучив этот язык запросов к XML-данным, я пришел к выводу, что это очень мощный инструмент для извлечения информации из XML. И рассказать о нем в одной статье было бы не очень хорошо с моей стороны. Таким образом я продолжаю тему парсинга XML-файлов, очень актуальную в наше время.

А начата эта тема несколькими постами ранее:
Первый post о том, как я научился парсить XML
Про SimpleXML

Надеюсь, вы их не пропустили 😉

Итак, сначала расскажу, как выполнять XPath-выражения в PHP, а потом - про сами выражения. Для выполнения XPath я воспользовался двумя способами:

  1. Функциями DOM XML
  2. Классом SimpleXML

Использование DOM XML

Сразу приведу пример:

test.xml

<?xml version="1.0" encoding="utf-8" ?>
<books>
  <book>
    <title>Название книги</title>
    <author>Имя и фамилия автора</author>
    <pages>Количество страниц</pages>
    <isbn>ISBN</isbn>
    <year>Год издания</year>
  </book>
</books>

test.php

<?
    $dom = domxml_open_file('test.xml');
   
    $xpath = xpath_new_context($dom);
   
    // выполняем выражение
    $nodes = xpath_eval($xpath, '/books/book/*');
   
    foreach ($nodes->nodeset as $node) {
        // извлекаем свойства узлов, например: $node->tagname - имя тега
    }
?>

Т.е. сначала мы парсим XML, потом создаем контекст XPath (объект класса XPathContext), который передаем потом в функцию xpath_eval, выполняющую наше выражение и возвращающую результат в виде объекта класса XPathObject. Далее мы используем этот объект для извлечения информации. Свойство nodeset содержит набор узлов, которые были получены в результате выполнения запроса. Чтобы просмотреть, что из себя представляет объект $nodes, можно использовать функцию var_dump.

Использование SimpleXML

<?
    $xml = simplexml_load_file('test.xml');
   
    $nodes = $xml->xpath('/books/book/*');
   
    foreach ($nodes as $node) {
        echo $node; // выводим текстовое содержимое узла
    }
?>

Отличие от первого метода здесь в том, что узлы возвращаются не в «сыром» виде, т.е. сразу возвращается текстовое содержимое узла без всякой служебной информации (имя тега, например).

Выражения

Теперь мы можем извлекать нужную информацию по заданному выражению. А что же представляют из себя сами выражения?

Будем рассматривать их для представленного примера с книгами.

Чтобы нам выбрать корневой узел books, достаточно написать /books.
Чтобы выбрать все книги, можно пойти двумя путями:
/books/book - выбирает все элементы book, дочерние по отношению к books
//book - выбирает все элементы book, независимо от родителя

Или, например, мы хотим выбрать все ISBN: //book/ISBN

Выражение //* означает выборку ВСЕХ элементов.

Выражение /books/book/* выберет все элементы, находящиеся во всех книгах. Т.е. например:

<?xml version="1.0" encoding="utf-8" ?>
<books>
  <book>
    <title>Название книги1</title>
    <author>Имя и фамилия автора1</author>
    <pages>Количество страниц1</pages>
    <isbn>ISBN1</isbn>
    <year>Год издания1</year>
  </book>
  <book>
    <title>Название книги2</title>
    <author>Имя и фамилия автора2</author>
    <pages>Количество страниц2</pages>
    <isbn>ISBN2</isbn>
    <year>Год издания2</year>
  </book>
</books>

Для этого примера запрос /books/book/* выберет элементы:

Имя и фамилия автора1

Количество страниц1 ISBN1
Год издания1

Имя и фамилия автора2

Количество страниц2 ISBN2
Год издания2

Элемент title можно выбрать еще вот так:
/*/*/title

Т.е. этот запрос означает, что нам нужно выбрать элемент title, который имеет родителя и прародителя.

Вообще, как вы заметили, звездочка (*) означает «любой элемент».

Чтобы нам задать определенный элемент по счету из множества выборки, можем это указать в квадратных скобках:

//book[1] - выберет первую книгу

Или есть еще функция last():

//book[last()] - выберет последнюю книгу

А как же быть с атрибутами элементов? Для этого тоже предусмотрены шаблоны в выражениях. Сейчас мы их и рассмотрим. Пусть книга у нас описывается немного по-другому:

<?xml version="1.0" encoding="utf-8" ?>
<books>
  <book isbn=”ISBN”>
    <title>Название книги</title>
    <author>Имя и фамилия автора</author>
    <pages>Количество страниц</pages>
    <year>Год издания</year>
  </book>
</books>

Т.е. мы тут перенесли ISBN в атрибут книги.

Теперь запросы:

//@isbn - выбирает все атрибуты isbn
//book[@isbn] - выбирает все книги, имеющие атрибут isbn
//book[@*] - выбирает все книги, имеющие хотя бы один атрибут
//book[not(@*)] - выбирает все книги, не имеющие ни одного атрибута
//book[@isbn=”ISBN”] - выбирает все книги, имеющие атрибут isbn со значением ISBN
//book[normalize-space(@isbn)=”ISBN”] - выбирает все книги, имеющие атрибут isbn со значением ISBN. При этом удаляются все пробелы с начала и конца заданной строки (значение атрибута isbn)

Это лишь самые простые запросы с атрибутами. Кроме функций normalize-space и not есть еще множество функций, позволяющих работать со строками и выражениями довольно эффективно.

По поводу функций, работающих с элементами, можно еще сказать про следующие:

Функция count() - возвращает количество элементов в указанном множестве:
//*[count(author)=2] - вернет все элементы, имеющие два дочерних элемента author
//*[count(*)=2] - вернет все элементы, имеющие два произвольных дочерних элемента

Функция name() - вернет имя элемента:
//*[name()=’book’] - эквивалентно //book

Функция starts-with(строка1, строка2) - вернет true, если строка1 начинается со строки2:
//*[starts-with(name(),’auth’)] - вернет все элементы author

Функция contains(строка1, строка2) - вернет true, если строка1 содержит в себе строку2:
//*[contains(name(),’ge’)] - вернет все элементы pages

Функция string-length() - возвращает количество символов в заданной строке:
//*[string-length(name()) = 5] - вернет элементы title, pages, books
//*[string-length(name()) < 5] - вернет элементы book, year

Да, еще забыл сказать, что выражения можно комбинировать. Например:
/books/book/title | /books/book/author - выберет все элементы title и author

При этом ограничений на количество комбинаций нет.

Заключение

Это все, что я хотел рассмотреть в первой части. В следующей мы рассмотрим такое понятие, как «оси» в XPath. И затронем некоторые математические функции. До встречи!





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



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

  1. Спасибо, интересный материал, сам экспериментировал с этим делом, но менее удачно :)

  2. novice

    Пожалуйста 😉 Но это еще не все, что я хотел сказать про XPath. Ждите второй части.

  3. Novice, а изучать аякс, подробно описывая, ты не собираешься? :) . Тема сейчас очень востребованая и очень актуальная.

  4. novice

    Да. Надо как-нибудь занятся ajax-ом. Правда я плохо javascript знаю. Нужно будет начинать с него :)

  5. В свободное время сейчас почитываю книгу “AJAX и PHP. разработка динамических приложений”, вроде неплохо написана, доступно. В интернете лежит, можно скачать, если заинтересует :) .

  6. novice

    Хм. Даже книжка отдельная есть :) Спасибо за совет. Посмотрю ее обязательно.

  7. Чтоб долго не искал, держи, на блоге в посте оставил ссылку на книгу :).
    [ссылка]

  8. novice

    Ох, спасибо :) Обязательно скачаю!

  9. Юрий Изотов

    Да ajax это один обьект xmlhttprequest :) Другое дело - изобретательность разработчиков в его применении.

  10. novice

    Я сперва тоже начал с работы с этим классом (xmlhttprequest). Потом наткнулся на библиотеку-интерфейс для javascript`а - jQuery (jquery.com). С ней легче скрипты писать гораздо javascript и у нее есть поддержка ajax (думаю тоже удобная). Напишу про это пару постов обязательно. Насчет изобретательности разработчиков - это точно :) Советую взглянуть на ajaxrain.com - там классные вещи очень есть.

  11. Юрий Изотов

    Ну, батенька, jquery это уже вроде JSON, не XML. Хотя Вы правы, с либой легче в принципе. Тут дело вкуса.

  12. novice

    [ссылка]
    Я так понял, что там можно вообще несколькими способами данные передавать асинхронно. И JSON тоже и xml, и xhtml.

  13. Mark

    Скажите пожалуйста почему генерируется ошибка
    “Fatal error: Call to undefined function domxml_open_file() in Z:\home\localhost\www\project\tre.php on line 2″

  14. novice

    Mark, скорее всего из-за того, что Вы используете версию PHP < 4.2.0, либо скорее всего у Вас не установлено расширение DOMXML.

  15. Tempter

    Такой вопрос:
    а если хмл файл очень большой, т.е. несколько гигов, как можно пропарсить его в таком случае? Ведь считать его целиком в какой нить объект или переменную нельзя.

  16. novice

    Читайте здесь:
    https://i-novice.net/kak-ya-nauchilsya-parsit-xml-v-php/
    Этот метод подойдет для парсинга больших файлов

  17. Руслан

    Спасибо за статью! Как раз то, что я искал!!!!!!

  18. Спасибо, очень хорошо расписано среди всех подобных обьяснения насчет выражений с примерами.

  19. А как быть в случае пхп5? там ведь вроде функции domxml_open_file не предусмотрено…

  20. Фак

    Унылое говно! Хули вы все запретили копировать твари, ебанутые.


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