PHP+XML+XPath. Часть 1
Вот я и добрался до XPath, как и обещал. Изучив этот язык запросов к XML-данным, я пришел к выводу, что это очень мощный инструмент для извлечения информации из XML. И рассказать о нем в одной статье было бы не очень хорошо с моей стороны. Таким образом я продолжаю тему парсинга XML-файлов, очень актуальную в наше время. |
А начата эта тема несколькими постами ранее:
Первый post о том, как я научился парсить XML
Про SimpleXML
Надеюсь, вы их не пропустили 😉
Итак, сначала расскажу, как выполнять XPath-выражения в PHP, а потом - про сами выражения. Для выполнения XPath я воспользовался двумя способами:
- Функциями DOM XML
- Классом SimpleXML
Использование DOM XML
Сразу приведу пример:
test.xml
<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/* выберет все элементы, находящиеся во всех книгах. Т.е. например:
<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/* выберет элементы:
Элемент title можно выбрать еще вот так:
/*/*/title
Т.е. этот запрос означает, что нам нужно выбрать элемент title, который имеет родителя и прародителя.
Вообще, как вы заметили, звездочка (*) означает «любой элемент».
Чтобы нам задать определенный элемент по счету из множества выборки, можем это указать в квадратных скобках:
//book[1] - выберет первую книгу
Или есть еще функция last():
//book[last()] - выберет последнюю книгу
А как же быть с атрибутами элементов? Для этого тоже предусмотрены шаблоны в выражениях. Сейчас мы их и рассмотрим. Пусть книга у нас описывается немного по-другому:
<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. И затронем некоторые математические функции. До встречи!
Спасибо, интересный материал, сам экспериментировал с этим делом, но менее удачно
Пожалуйста 😉 Но это еще не все, что я хотел сказать про XPath. Ждите второй части.
Novice, а изучать аякс, подробно описывая, ты не собираешься? . Тема сейчас очень востребованая и очень актуальная.
Да. Надо как-нибудь занятся ajax-ом. Правда я плохо javascript знаю. Нужно будет начинать с него
В свободное время сейчас почитываю книгу “AJAX и PHP. разработка динамических приложений”, вроде неплохо написана, доступно. В интернете лежит, можно скачать, если заинтересует .
Хм. Даже книжка отдельная есть Спасибо за совет. Посмотрю ее обязательно.
Чтоб долго не искал, держи, на блоге в посте оставил ссылку на книгу :).
[ссылка]
Ох, спасибо Обязательно скачаю!
Да ajax это один обьект xmlhttprequest Другое дело - изобретательность разработчиков в его применении.
Я сперва тоже начал с работы с этим классом (xmlhttprequest). Потом наткнулся на библиотеку-интерфейс для javascript`а - jQuery (jquery.com). С ней легче скрипты писать гораздо javascript и у нее есть поддержка ajax (думаю тоже удобная). Напишу про это пару постов обязательно. Насчет изобретательности разработчиков - это точно Советую взглянуть на ajaxrain.com - там классные вещи очень есть.
Ну, батенька, jquery это уже вроде JSON, не XML. Хотя Вы правы, с либой легче в принципе. Тут дело вкуса.
[ссылка]
Я так понял, что там можно вообще несколькими способами данные передавать асинхронно. И JSON тоже и xml, и xhtml.
Скажите пожалуйста почему генерируется ошибка
“Fatal error: Call to undefined function domxml_open_file() in Z:\home\localhost\www\project\tre.php on line 2″
Mark, скорее всего из-за того, что Вы используете версию PHP < 4.2.0, либо скорее всего у Вас не установлено расширение DOMXML.
Такой вопрос:
а если хмл файл очень большой, т.е. несколько гигов, как можно пропарсить его в таком случае? Ведь считать его целиком в какой нить объект или переменную нельзя.
Читайте здесь:
https://i-novice.net/kak-ya-nauchilsya-parsit-xml-v-php/
Этот метод подойдет для парсинга больших файлов
Спасибо за статью! Как раз то, что я искал!!!!!!
Спасибо, очень хорошо расписано среди всех подобных обьяснения насчет выражений с примерами.
А как быть в случае пхп5? там ведь вроде функции domxml_open_file не предусмотрено…
Унылое говно! Хули вы все запретили копировать твари, ебанутые.