Что такое парсер xml. Для чего нужны XML-парсеры и чем они могут быть полезны

Я видел много xml parser`ов, но не затрагивал при этом веб-программирование. Теперь же я хочу выяснить и научиться вместе с вами, как сделать простой xml parser в php.

А зачем? Надо!

Не, ну на самом деле: xml-файлы — очень полезная штука. И любой профессионал должен… нет, не должен, а обязан знать, как с ними работать. Мы же хотим стать профессионалами? Если Вы на моем блоге, то такое желание у Вас есть.

Мы предполагаем, что знаем, что такое XML и описывать его здесь не будем. Ну, если не знаем, то легко узнаем здесь: http://ru.wikipedia.org/wiki/XML

При поиске способов парсинга XML на PHP, я обнаружил простой набор функций в PHP для работы с XML-файлами, который называется «XML Parser Functions». Парсинг начинается с инициализации парсера вызовом функции xml_parser_create:

$xml_parser = xml_parser_create();

Потом нам нужно сказать парсеру, какие функции будут обрабатывать попадающиеся ему xml-теги и текстовую информацию в процессе парсинга. Т.е. нужно установить некие обработчики:

xml_set_element_handler($xml_parser, “startElement”, “endElement”);

Эта функция отвечает за установку обработчиков начала элемента и конца элемента. Например, если в тексте xml-файла встретится комбинация, то функция startElement сработает, когда парсер найдет элемент, а функция endElement — при нахождении.

Сами же функции startElement и endElement принимают несколько параметров согласно документации по php:

А как же считывать данные из файла? Мы же пока не видели ни одного параметра для этого ни в одной из функций! А об этом дальше: считывание файла возлагается на плечи программиста, т.е. мы должны использовать стандартные функции для работы с файлами:

Открыли файл. А теперь нужно построчно считывать его и скармливать считываемые строки функции xml_parse:

XML Error: ".xml_error_string(xml_get_error_code($xml_parser)); echo " at line ".xml_get_current_line_number($xml_parser); break; } } ?>

Здесь заметим две очень важные вещи. Первая — это то, что функции xml_parse в третьем параметре нужно передать флаг считывания последней строки (true — если строка последняя, false — если нет). Второе — это то, что как и в любом деле, мы должны следить здесь за ошибками. За это отвечают функции xml_get_error_code и xml_error_string. Первая функция получает код ошибки, а вторая — по полученному коду возвращает текстовое описание ошибки. Что в результате возникновения ошибки получится — рассмотрим позже. Не менее полезная функция xml_get_current_line_number скажет нам номер текущей обрабатываемой строки в файле.

И как всегда мы должны освободить занимаемые системой ресурсы. Для парсинга XML — это функция xml_parser_free:

xml_parser_free($xml_parser);

Вот, основные функции мы рассмотрели. Пора бы посмотреть их на деле. Для этого я придумал xml-файл с очень простой структурой:




123

71234567890

Назовем этот файл data.xml и попытаемся его распарсить с помощью следующего кода:

Element: $name
"; // имя элемента $depth++; // увеличиваем глубину, чтобы браузер показал отступы foreach ($attrs as $attr => $value) { echo str_repeat(" ", $depth * 3); // отступы // выводим имя атрибута и его значение echo "Attribute: ".$attr." = ".$value."
"; } } function endElement($parser, $name) { global $depth; $depth--; // уменьшаем глубину } $depth = 0; $file = "data.xml"; $xml_parser = xml_parser_create(); xml_set_element_handler($xml_parser, "startElement", "endElement"); if (!($fp = fopen($file, "r"))) { die("could not open XML input"); } while ($data = fgets($fp)) { if (!xml_parse($xml_parser, $data, feof($fp))) { echo "
XML Error: "; echo xml_error_string(xml_get_error_code($xml_parser)); echo " at line ".xml_get_current_line_number($xml_parser); break; } } xml_parser_free($xml_parser); ?>

В результате разработанного нами простейшего скрипта браузер вывел в свое окно следующую информацию:

Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS

Attribute: KVARTIRA = 12
Attribute: DOM = 15
Element: PHONE

Попробуем испортить XML-файл, заменив тег На , а закрывающий тег оставив прежним:

Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS
Attribute: ULICA = моя улица!!
Attribute: KVARTIRA = 12
Attribute: DOM = 15
Element: TELEPHONE

XML Error: Mismatched tag at line 5

Ух ты! Сообщения об ошибках работают! Причем довольно информативные.

Эх, я забыл еще одну вещь… Мы же не вывели текст, содержащийся внутри тегов address и phone. Исправляем наш недочет — добавляем текстовый обработчик с помощью функции xml_set_character_data_handler:

xml_set_character_data_handler($xml_parser, ’stringElement’);

И добавляем в код саму функцию-обработчик:

0) { global $depth; echo str_repeat(" ", $depth * 3); // отступ echo "String: ".$str."
"; // выводим строку } } ?>

Посмотрим теперь на вывод:

Element: ROOT
Element: INFO
Attribute: WHO = моя
Element: ADDRESS
Attribute: ULICA = моя улица!!
Attribute: KVARTIRA = 12
Attribute: DOM = 15
String: 123
Element: PHONE
String: +71234567890

О! Теперь вывели все!

Кстати, кто-нибудь заметил, что имена тегов и атрибутов все большими буквами написаны? Странно… они же в нашем xml-файле малыми буквами обозначены. Видимо где-то какие-то настройки установлены, чтобы делать uppercase…

Ааа, нашел! Оказывается есть еще функция xml_parser_set_option:

xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);

Таким вызовом мы отменяем вывод имен атрибутов и имен тегов большими буквами:

Element: root
Element: info
Attribute: who = моя
Element: address
Attribute: ulica = моя улица!!
Attribute: kvartira = 12
Attribute: dom = 15
String: 123
Element: phone
String: +71234567890

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

Вот и лето пришло и первая неделя июля пролетела незаметно. Через две недели мне защищать свой диплом, а одна из частей его – XML parser . Но жалко, что не в пхп. Ну ниче, счас наверстаем

Я видел много xml parser`ов, но не затрагивал при этом веб-программирование. Теперь же я хочу выяснить и научиться вместе с вами, как сделать простой xml parser в php.

А зачем? Надо!

Не, ну на самом деле: xml-файлы – очень полезная штука. И любой профессионал должен… нет, не должен, а обязан знать, как с ними работать. Мы же хотим стать профессионалами? Если Вы на моем блоге, то такое желание у Вас есть.

Мы предполагаем, что знаем, что такое XML и описывать его здесь не будем. Ну, если не знаем, то легко узнаем здесь: http://ru.wikipedia.org/wiki/XML

При поиске способов парсинга XML на PHP, я обнаружил простой набор функций в PHP для работы с XML-файлами, который называется «XML Parser Functions ». Парсинг начинается с инициализации парсера вызовом функции xml_parser_create:

$xml_parser = xml_parser_create();

Потом нам нужно сказать парсеру, какие функции будут обрабатывать попадающиеся ему xml-теги и текстовую информацию в процессе парсинга. Т.е. нужно установить некие обработчики:

xml_set_element_handler($xml_parser, “startElement”, “endElement”);

Эта функция отвечает за установку обработчиков начала элемента и конца элемента. Например, если в тексте xml-файла встретится комбинация, то функция startElement сработает, когда парсер найдет элемент, а функция endElement – при нахождении.

Сами же функции startElement и endElement принимают несколько параметров согласно документации по php:



// (т.к. мы можем использовать несколько парсеров)

// $attrs - массив атрибутов обнаруженного элемента
}function endElement($parser, $name) {
// $parser - уникальный идентификатор парсера
// $name - имя обнаруженного элемента
}
?>

А как же считывать данные из файла? Мы же пока не видели ни одного параметра для этого ни в одной из функций! А об этом дальше: считывание файла возлагается на плечи программиста, т.е. мы должны использовать стандартные функции для работы с файлами:

Открыли файл. А теперь нужно построчно считывать его и скармливать считываемые строки функции xml_parse:

Здесь заметим две очень важные вещи. Первая – это то, что функции xml_parse в третьем параметре нужно передать флаг считывания последней строки (true – если строка последняя, false – если нет). Второе – это то, что как и в любом деле, мы должны следить здесь за ошибками. За это отвечают функции xml_get_error_code и xml_error_string. Первая функция получает код ошибки, а вторая – по полученному коду возвращает текстовое описание ошибки. Что в результате возникновения ошибки получится – рассмотрим позже. Не менее полезная функция xml_get_current_line_number скажет нам номер текущей обрабатываемой строки в файле.

И как всегда мы должны освободить занимаемые системой ресурсы. Для парсинга XML – это функция xml_parser_free:

xml_parser_free($xml_parser);

Вот, основные функции мы рассмотрели. Пора бы посмотреть их на деле. Для этого я придумал xml-файл с очень простой структурой:




123

71234567890

Назовем этот файл data.xml и попытаемся его распарсить с помощью следующего кода:

function startElement($parser, $name, $attrs) {
global $depth;echo str_repeat (" ", $depth * 3); // отступы
echo "Element: $name
"; // имя элемента

$depth++; // увеличиваем глубину, чтобы браузер показал отступы

xml_set_element_handler ($xml_parser, "startElement", "endElement");

if (!($fp = fopen ($file, "r"))) {
die ("could not open XML input");
}

while ($data = fgets ($fp)) {
if (!xml_parse ($xml_parser, $data, feof ($fp))) {
echo "
XML Error: ";
echo xml_error_string (xml_get_error_code ($xml_parser));
echo " at line ".xml_get_current_line_number ($xml_parser);
break;
}
}

xml_parser_free ($xml_parser);
?>

В результате разработанного нами простейшего скрипта браузер вывел в свое окно следующую информацию:

Element: ROOT Element: INFO Attribute: WHO = моя Element: ADDRESS Attribute: ULICA = моя улица!! Attribute: KVARTIRA = 12 Attribute: DOM = 15 Element: PHONE

Попробуем испортить XML-файл, заменив тег На , а закрывающий тег оставив прежним:

Element: ROOT Element: INFO Attribute: WHO = моя Element: ADDRESS Attribute: ULICA = моя улица!! Attribute: KVARTIRA = 12 Attribute: DOM = 15 Element: TELEPHONE

XML Error: Mismatched tag at line 5

Ух ты! Сообщения об ошибках работают! Причем довольно информативные.

Эх, я забыл еще одну вещь… Мы же не вывели текст, содержащийся внутри тегов address и phone. Исправляем наш недочет – добавляем текстовый обработчик с помощью функции xml_set_character_data_handler:

xml_set_character_data_handler($xml_parser, ’stringElement’);

И добавляем в код саму функцию-обработчик:

Посмотрим теперь на вывод:

Element: ROOT Element: INFO Attribute: WHO = моя Element: ADDRESS Attribute: ULICA = моя улица!! Attribute: KVARTIRA = 12 Attribute: DOM = 15 String: 123 Element: PHONE String: +71234567890

О! Теперь вывели все!

Кстати, кто-нибудь заметил, что имена тегов и атрибутов все большими буквами написаны? Странно… они же в нашем xml-файле малыми буквами обозначены. Видимо где-то какие-то настройки установлены, чтобы делать uppercase…

Ааа, нашел! Оказывается есть еще функция xml_parser_set_option:

xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);

Таким вызовом мы отменяем вывод имен атрибутов и имен тегов большими буквами:

Element: root Element: info Attribute: who = моя Element: address Attribute: ulica = моя улица!! Attribute: kvartira = 12 Attribute: dom = 15 String: 123 Element: phone String: +71234567890

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

nika, Вам нужно вставить вызов
xml_set_character_data_handler($xml_parser, ’stringElement’);
после
xml_set_element_handler($xml_parser, “startElement”, “endElement”);

В общем для правильного отображения нужно соблюдать следующее:
1. нужно, чтобы xml-файл был в кодировке UTF-8
2. в xml-файле первая строчка должна быть такой:
< ?xml version="1.0" encoding="UTF-8"?>
3. нужно инициализировать парсер так:
xml_parser_create(”UTF-8?);
4. нужно перед выводом содержимого xml-файла в браузер настроить последний на кодировку UTF-8:
header(“Content-Type: text/html; charset=utf-8?);

Для отображения текста на русском языке используем
iconv(”UTF-8?,”windows-1251?, $attr), где $attr, то что нужно вывести.

Есть такие два класса интересных в стандартной библеотеке PHP5 – DOMDocument() и XSLTProcesor() использовал на довольно крупных проектах – не жалуюсь =)

Вообще у PHP есть еще одна библиотечка для разбора XML по принципу SAX – называется XMLReader.

    Господа, как вывести из xml с помощью simplexml не стандартные теги, например?
    Переменная вида $text = $item->yandex:full-text не работает.

    Все ОК, только обратите внимание на наличие следующего параметра,

    function cdata($parser, $cdata)
    {
    var_dump($parser, $cdata);
    }

    xml_set_character_data_handler($this->parser, “cdata”);

    Без него не хочет парсить XML с CDATA…. размером в 1.5 метра

    2Nika, Функция header() работает только в том случае, если до нее не было ничего выведено, то бишь первым действие производили ее, т.к. мы отсылаем заголовки на эту страницу, которые говорят нам, что текст нужно переводить в кодировку UTF-8. Судя по вашей ошибке у вас есть пропуски строк, поэтому сделайте вот так:

    Значение == порядковый номер строки
    header(”Content-Type: text/html; charset=utf-8?); == 2

    Всё намного проще делается в действительности
    yurban.ru/development/php_xml_parser

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

Используя расширение SimpleXML в PHP, которое было добавлено еще в PHP 5.0, работать с XML очень легко и просто. В этой статье я покажу вам, как это делать.

Основы использования

Давайте начнем со следующего примера languages.xml :


>

> 1972>
> Dennis Ritchie>
>

> 1995>
> Rasmus Lerdorf>
>

> 1995>
> James Gosling>
>
>

Данный XML-документ содержит список языков программирования с некоторой информацией о каждом языке: год его реализации и имя его создателя.

Первый шаг заключается в загрузке XML с использованием функций либо simplexml_load_file() , либо simplexml_load_string() . Как понятно из названия функций, первая загружает XML из файла, а вторая будет загружать XML из строки.

Обе функции считывают все дерево DOM в память и возвращают объект SimpleXMLElement . В приведенном выше примере, объект сохраняется в переменной $languages. Вы можете использовать функции var_dump() или print_r() , чтобы получить подробную информацию о возвращенном объект, если хотите.

SimpleXMLElement Object
[ lang] => Array
[ 0 ] => SimpleXMLElement Object
[ @ attributes] => Array
[ name] => C
[ appeared] => 1972
[ creator] => Dennis Ritchie
[ 1 ] => SimpleXMLElement Object
[ @ attributes] => Array
[ name] => PHP
[ appeared] => 1995
[ creator] => Rasmus Lerdorf
[ 2 ] => SimpleXMLElement Object
[ @ attributes] => Array
[ name] => Java
[ appeared] => 1995
[ creator] => James Gosling
)
)

Данный XML содержит корневой элемент languages , внутри которого находятся три элемента lang. Каждый элемент массива соответствует элементу lang в XML-документе.

Вы можете получить доступ к свойствам объекта при помощи оператора -> . Например, $languages->lang вернет вам объект SimpleXMLElement, который соответствует первому элементу lang . Этот объект содержит два свойства: appeared и creator.

$languages -> lang [ 0 ] -> appeared ;
$languages -> lang [ 0 ] -> creator ;

Вывести на экран список языков и показать их свойства можно очень легко при помощи стандартного цикла, такого как foreach .

foreach ($languages -> lang as $lang ) {
printf (
"" ,
$lang [ "name" ] ,
$lang -> appeared ,
$lang -> creator
) ;
}

Обратите внимание, как я получил доступ к имени атрибута элемента lang, чтобы получить название языка. Таким образом Вы можете получить доступ к любому атрибуту элемента представленого в виде объекта SimpleXMLElement.

Работа с пространствами имен

Во время работы с XML различных веб-сервисов вы не раз столкнетесь с пространствами имен элементов. Давайте изменим наш languages.xml , чтобы показать пример использования пространства имен:



xmlns:dc =>

> 1972>
> Dennis Ritchie>
>

> 1995>
> Rasmus Lerdorf>
>

> 1995>
> James Gosling>
>
>

Теперь элемент creator помещается в пространстве имен dc , который указывает на http://purl.org/dc/elements/1.1/. Если вы попытаетесь распечатать создателей языка, используя наш предыдущий код, то он не будет работать. Для того, чтобы читать пространства имен элементов вам необходимо использовать один из следующих подходов.

Первый подход заключается в использовании имен URI непосредственно в коде, когда обращаемся к пространству имен элементов. В следующем примере показано, как это делается:

$dc = $languages -> lang [ 1 ] - > children("http://purl.org/dc/elements/1.1/" ) ;
echo $dc -> creator ;

Метод children() принимает пространство имен и возвращает дочерние элементы, которые начинаются с префикса. Он принимает два аргумента, первый из которых является пространством имен XML, и второй необязательный аргумент, который по умолчанию равен false . Если второй аргумент установлен как TRUE, пространство имен будет рассмотрено как префикс. Если FALSE, то пространство имен будет рассмотрено как пространство имен URL.

Второй подход состоит в чтении имен URI из документа и использование их при обращении к пространству имен элементов. На самом деле это лучший способ доступа к элементам, потому что вы не должны быть жестко привязаны к URI.

$namespaces = $languages -> getNamespaces (true ) ;
$dc = $languages -> lang [ 1 ] -> children ($namespaces [ "dc" ] ) ;

echo $dc -> creator ;

Метод GetNamespaces() возвращает массив имен префиксов и связанные с ними URI. Он принимает дополнительный параметр, который по умолчанию равен false . Если вы установите его как true , то этот метод будет возвращать имена, используемые в родительских и дочерних узлах. В противном случае, он находит пространства имен, используемые только в родительском узле.

Теперь вы можете пройтись по списку языков следующим образом:

$languages = simplexml_load_file ("languages.xml" ) ;
$ns = $languages -> getNamespaces (true ) ;

foreach ($languages -> lang as $lang ) {
$dc = $lang -> children ($ns [ "dc" ] ) ;
printf (
"

%s появился в %d и был создан %s .

" ,
$lang [ "name" ] ,
$lang -> appeared ,
$dc -> creator
) ;
}

Практический пример - Парсинг видео-канала с YouTube

Давайте рассмотрим пример, который получает RSS-feed с канала YouTube и отображает ссылки на все видео из него. Для этого нужно обратится по следующему адресу:

http://gdata.youtube.com/feeds/api/users/xxx/uploads

URL возвращает список последних видео с данного канала в формате XML. Мы распарсим XML и получим следующую информацию для каждого видео:

  • Ссылка на видео
  • Миниатюра
  • Название

Мы начнем с поиска и загрузки XML:

$channel = "Имя_канала" ;
$url = "http://gdata.youtube.com/feeds/api/users/" . $channel . "/uploads" ;
$xml = file_get_contents ($url ) ;

$feed = simplexml_load_string ($xml ) ;
$ns = $feed -> getNameSpaces (true ) ;

Если вы посмотрите на XML-feed, то вы можете увидеть, что там есть несколько элементов entity , каждый из которых хранит подробную информацию о конкретном видео с канала. Но мы используем только миниатюры изображений, адрес видео и название. Эти три элемента являются потомками элемента group , который, в свою очередь, является дочерним для entry :

>

>



Title…>

>

>

Мы просто пройдемся по всем элементам entry , и для каждого из них извлечем необходимую информацию. Обратите внимание, что player, thumbnail и title находятся в пространстве имен media. Таким образом, мы должны действовать, как в предыдущем примере. Мы получаем имена из документа и используем пространство имен при обращении к элементам.

foreach ($feed -> entry as $entry ) {
$group = $entry -> children ($ns [ "media" ] ) ;
$group = $group -> group ;
$thumbnail_attrs = $group -> thumbnail [ 1 ] -> attributes () ;
$image = $thumbnail_attrs [ "url" ] ;
$player = $group -> player -> attributes () ;
$link = $player [ "url" ] ;
$title = $group -> title ;
printf ("

" ,
$player , $image , $title ) ;
}

Заключение

Теперь, когда вы знаете, как использовать SimpleXML для разбора XML-данных, вы можете улучшить свои навыки путем анализа различных XML-каналов с различными API. Но важно учитывать, что SimpleXML считывает весь DOM в память, так что если вы парсите большой набор данных, то вы можете столкнуться с нехваткой памяти. Чтобы узнать больше о SimpleXML прочтите документацию .


Если у Вас возникли вопросы, то для скорейшего получения ответа рекомендуем воспользоваться нашим

XML для PHP-разработчиков

Часть 2. Расширенные методы парсинга XML

Методы синтаксического анализа объемных или сложных XML-документов в PHP5

Серия контента:

В PHP5 увеличено разнообразие методов синтаксического анализа (парсинга) XML. Парсер Expat SAX Джеймса Кларка, который теперь основан на библиотеке libxml2, больше не является единственным полнофункциональным парсером. Нам уже знакома возможность парсинга посредством DOM в полном согласии со стандартом W3C. Дополнительные возможности предлагает SimpleXML, который мы рассмотрели в части 1 (см. ), и XMLReader, который проще в понимании и быстрее в работе, чем SAX. Все расширения XML теперь основаны на библиотеке libxml2 проекта GNOME. Эта унифицированная библиотека позволяет разным расширениям взаимодействовать друг с другом. В настоящей статье обсуждаются методы парсинга XML в РНР5 с акцентом на объемные или сложные XML-документы. В ней рассказывается о методах парсинга и о критериях выбора, когда он есть, оптимального метода для синтаксического анализа XML-документов разного типа.

SimpleXML

В первой статье этой серии содержится важная информация об XML и инструкции по началу работы с интерфейсами прикладных программ (API). В ней демонстрировалось, что SimpleXML в сочетании с Document Object Model (DOM) служит идеальным инструментом для разработчиков, имеющих дело с простыми, предсказуемыми и относительно компактными XML-документами.

XML и PHP5

Язык Extensible Markup Language (XML) можно назвать и языком разметки, и форматом хранения текстовых данных; он предоставляет текстовые средства для описания древовидных структур и их применения к информации.

В РНР5 имеются как новые, так и переписанные расширения для XML-парсинга. SimpleXML, DOM и процессор XSLT загружают в память весь XML-документ целиком. Simple API for XML (SAX) и XMLReader относятся к тем парсерам, которые обрабатывают XML-документ по частям. SAX функционирует так же, как в РНР4, но теперь он основан на библиотеке libxml2, а не expat. Тем, кто знаком с DOM по другим языкам, в РНР5 будет легче с ним работать, чем в предыдущих версиях РНР.

Основы XML-парсинга

Существует два основных способа XML-парсинга: на базе деревьев и на базе потоков. Метод дерева предусматривает загрузку в память всего XML-документа целиком. Древовидная структура файла позволяет произвольно обращаться к элементам документа и редактировать XML. Примерами парсеров по методу дерева служат DOM и SimpleXML. Они хранят древовидную структуру в памяти в разных, но взаимодействующих форматах. При поточном парсинге весь документ в память не загружается. В данном случае термин «поток» употребляется в том же смысле, что и при описании поточного аудио. Происходит то же самое и по тем же причинам: данные поступают мелкими порциями с целью экономии полосы пропускания и ресурсов памяти. При поточном парсинге доступен только тот узел, который анализируется в данный момент, а редактирование XML-документа целиком невозможно. Примерами поточных парсеров служат XMLReader и SAX.

Парсеры, работающие по методу дерева

Парсеры, работающие по методу дерева, загружают в память весь документ, так что корень напоминает ствол дерева, а все дочерние, внучатые и более отдаленные потомки и атрибуты служат ветвями. Самый известый парсер, работающий по методу дерева, это DOM. Самый простой — SimpleXML. Рассмотрим тот и другой.

Парсинг при помощи DOM

Стандарт DOM, согласно W3C, представляет собой «...не зависящий от платформы и языка программирования интерфейс, который позволяет программам и сценариям динамически обращаться к документам и редактировать их содержание, структуру и стиль». Библиотека libxml2 проекта GNOME реализует DOM вместе со всеми его методами на языке С. Так как все XML-расширения РНР5 основаны на libxml2, они поддерживают полное взаимодействие друг с другом. Это взаимодействие значительно улучшает их функциональность. Например, можно извлечь элемент при помощи поточного парсера XMLReader, импортировать его в DOM и извлечь данные с использованием XPath. Подтверждение подобной гибкости продемонстрировано .

Парсер DOM работает по методу дерева. Он прост для понимания и применения, так как его структура в памяти напоминает оригинальный XML-документ. DOM передает информацию приложению, создавая дерево объектов, в точности повторяющее дерево элементов из XML-файла, так что каждый элемент XML служит узлом этого дерева. DOM – это стандарт W3C, что принесло ему признание разработчиков ввиду его согласованности с другими языками программирования. Так как DOM строит дерево всего документа, он потребляет большой объем памяти и много ресурсов процессора.

DOM в действии

Если ввиду какого-то ограничения вы вынуждены выбрать единственный парсер, имеет смысл выбрать DOM хотя бы в силу его гибкости. DOM позволяет составлять XML-документы, модифицировать их, обращаться к ним, проверять и преобразовывать их. При этом можно использовать все методы и свойства DOM.Большинство методов DOM второго уровня реализованы с надлежащей поддержкой свойств. Благодаря чрезвычайной гибкости DOM анализируемые документы могут быть чрезвычайно сложными. Однако помните, что за гибкость приходится платить тем, что весь документ загружается в память целиком.

В примере, приведенном в листинге 1, DOM применяется для парсинга документа и извлечения элемента при помощи функции getElementById . Перед ссылкой на идентификатор документ необходимо проверить, установив validateOnParse=true . Согласно стандарту DOM, для этого требуется DTD, который определяет атрибут ID как тип ID.

Листинг 1. Использование DOM с простым документом
validateOnParse = true; $doc->Load("basic.xml"); echo "The element whose id is myelement is: " . $doc->getElementById("myelement")->tagName . "\n"; ?>

Функция getElementsByTagName() возвращает новый экземпляр класса DOMNodeList , содержащий элементы с заданным именем тега. Конечно, нужно пролистать список. Изменение структуры документа во время работы со списком NodeList , выданным функцией getElementsByTagName() , влияет на список NodeList , с которым вы работаете (см. листинг 2). Проверка в данном случае не требуется.

Листинг 2. Метод DOM getElementsByTagName
DOMDocument { DOMNodeList getElementsByTagName(string name); }

В примере из листинга 3 DOM используется с XPath.

Листинг 3. Использование DOM и парсинг с применением XPath
preserveWhiteSpace = false; $doc->Load("book.xml"); $xpath = new DOMXPath($doc); // Мы начали с корневого элемента $query = "//book/chapter/para/informaltable/tgroup/tbody/row/entry[. = "en"]"; $entries = $xpath->query($query); foreach ($entries as $entry) { echo "Found {$entry->previousSibling->previousSibling->nodeValue}," . " by {$entry->previousSibling->nodeValue}\n"; } ?>

Высказав все эти приятные вещи по поводу DOM, я хочу привести пример того, что не надо делать с DOM, а затем, в следующем примере, показать, как это обойти. Листинг 4 иллюстрирует загрузку объемного файла в DOM только с целью извлечения данных из одного атрибута при помощи DomXpath.

Листинг 4. Неправильное использование DOM с XPath при работе с объемным XML-документом
load("tooBig.xml"); $xp = new DomXPath($dom); $result = $xp->query("/blog/entries/entry[@ID = 5225]/title") ; print $result->item(0)->nodeValue ."\n"; ?>

В последнем примере (листинг 5) DOM с XPath применяется точно так же, только данные передаются в XMLReader по одному элементу за раз с использованием метода expand() . При этом узел, переданный XMLReader , преобразуется в DOMElement .

Листинг 5. Правильное использование DOM с XPath при работе с объемным XML-документом
open("tooBig.xml"); while ($reader->read()) { switch ($reader->nodeType) { case (XMLREADER::ELEMENT): if ($reader->localName == "entry") { if ($reader->getAttribute("ID") == 5225) { $node = $reader->expand(); $dom = new DomDocument(); $n = $dom->importNode($node,true); $dom->appendChild($n); $xp = new DomXpath($dom); $res = $xp->query("/entry/title"); echo $res->item(0)->nodeValue; } } } } ?>

Парсинг при помощи SimpleXML

Расширение SimpleXML – еще один способ парсинга XML-документа. Для расширения SimpleXML требуется PHP5 и используется встроенная поддержка XPath. SimpleXML лучше всего работает с несложными данными XML. В том случае, если XML-документ не слишком сложен, глубок и не имеет смешанного контента, SimpleXML проще в применении, чем DOM, как и предполагает его название. Он интуитивно понятен, если вы работаете с известной структурой документа.

SimpleXML в действии

SimpleXML обладает многими преимуществами DOM и проще в программировании. Он позволяет легко обращаться к дереву XML, имеет встроенную поддержку проверки и XPath, а также взаимодействует с DOM, обеспечивая ему поддержку при чтении и записи XML-документов. Документы, анализируемые SimpleXML, пишутся легко и быстро. Однако помните, что, как и в случае DOM, простота и гибкость SimpleXML достигается ценой загрузки в память всего XML-документа целиком.

Код, приведенный в листинге 6, извлекает из примера XML-документа сюжет произведения, содержащийся в элементе

Листинг 6. Извлечение фрагмента текста
Great American Novel Cliff meets Lovely Woman. Loyal Dog sleeps, but wakes up to bark at mailman. 4 9 XML; ?> book->plot; // "Cliff meets Lovely Woman. ..." ?>

С другой стороны, может понадобиться извлечь многострочный адрес. Когда у одного родительского элемента имеется несколько экземпляров дочернего элемента, применяется обычная методика итерирования. Эта функциональность демонстрируется в листинге 7.

Листинг 7. Извлечение нескольких экземпляров элемента
Great American Novel Cliff meets Lovely Woman. 4 9 Man Bites Dog 22 3 XML; ?> book as $book) { echo $book->plot, "
"; } ?

Кроме чтения имен элементов и их значений, SimpleXML может обращаться к атрибутам элемента. В листинге 8 производится обращение к атрибутам элемента; это делается точно так же, как обращение к элементам массива.

Листинг 8. Демонстрация обращения SimpleXML к атрибутам элемента
Great American Novel Cliff meets Lovely Woman. 4 9 Man Bites Dog Reporter invents a prize-winning story. 22 3 XML; ?> book->success as $success) { switch((string) $success["type"]) { case "bestseller": echo $success, " months on bestseller list
"; break; case "bookclubs": echo $success, " bookclub listings
"; break; } } ?>

В последнем примере (см. листинг 9) SimpleXML и DOM используются с расширением XMLReader . При помощи XMLReader данные передаются последовательно, по одному элементу, с использованием метода expand() . Этим методом узел, переданный XMLReader , можно преобразовать в DOMElement , а затем передать SimpleXML.

Листинг 9. Использование SimpleXML и DOM с расширением XMLReader для анализа объемного XML-документа
open("tooBig.xml"); while ($reader->read()) { switch ($reader->nodeType) { case (XMLREADER::ELEMENT): if ($reader->localName == "entry") { if ($reader->getAttribute("ID") == 5225) { $node = $reader->expand(); $dom = new DomDocument(); $n = $dom->importNode($node,true); $dom->appendChild($n); $sxe = simplexml_import_dom($n); echo $sxe->title; } } } } ?>

Потоковые парсеры

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

Парсинг при помощи XMLReader

XMLReader — это потоковый парсер того типа, который часто называют курсорным или pull-парсером. XMLReader вытягивает информацию из XML-документа по требованию. Он основан на API, полученном из C# XmlTextReader. В PHP 5.1 он включен и задействован по умолчанию и основан на библиотеке libxml2. До выхода PHP 5.1 расширение XMLReader не было включено по умолчанию, но было доступно в PECL (см. ). XMLReader поддерживает пространства имен и проверку, включая DTD и Relaxed NG.

XMLReader в действии

Как потоковый парсер, XMLReader хорошо подходит для работы с объемными XML-документами; программировать в нем намного легче и обычно быстрее, чем в SAX. Это лучший потоковый парсер.

В следующем примере (листинг 10) объемный XML-документ анализируется при помощи XMLReader.

Листинг 10. XMLReader с объемным XML-файлом
open("tooBig.xml"); while ($reader->read()) { switch ($reader->nodeType) { case (XMLREADER::ELEMENT): if ($reader->localName == "entry") { if ($reader->getAttribute("ID") == 5225) { while ($reader->read()) { if ($reader->nodeType == XMLREADER::ELEMENT) { if ($reader->localName == "title") { $reader->read(); echo $reader->value; break; } if ($reader->localName == "entry") { break; } } } } } } } ?>

Парсинг при помощи SAX

Simple API for XML (SAX) представляет собой потоковый парсер. События связаны с читаемым XML-документом, поэтому SAX программируется в стиле обратных вызовов. Существуют события для открывающих и закрывающих тегов элемента, сущностей и ошибок парсинга. Главная причина использования парсера SAX вместо XMLReader заключается в том, что парсер SAX иногда более эффективен и обычно лучше знаком. Важный недостаток — код для парсера SAX получается сложнее, и его труднее писать, чем для XMLReader.

SAX в действии

SAX должен быть знаком тем, кто работал с XML в PHP4, а расширение SAX в PHP5 совместимо с версией, к которой они привыкли. Так как это потоковый парсер, он хорошо справляется с объемными файлами, но это не лучший выбор, чем XMLReader.

В листинге 11 приведен пример обработки объемного XML-документа парсером SAX.

Листинг 11. Использование SAX для анализа объемного XML-файла
hit = true; } else { $this->hit = false; } } if ($this->hit && $elementname == "title") { $this->titleHit = true; } else { $this->titleHit =false; } } //обратный вызов для конца каждого элемента function endElement($parser_object, $elementname) { } //обратный вызов для содержимого каждого элемента function contentHandler($parser_object,$data) { if ($this->titleHit) { echo trim($data)."
"; } } } //Функция запуска парсинга, когда все значения установлены //и файл открыт function doParse($parser_object) { if (!($fp = fopen("tooBig.xml", "r"))); //прокрутка данных while ($data = fread($fp, 4096)) { //анализ фрагмента xml_parse($parser_object, $data, feof($fp)); } } $SaxObject = new SaxClass(); $parser_object = xml_parser_create(); xml_set_object ($parser_object, $SaxObject); //Не меняйте регистр данных xml_parser_set_option($parser_object, XML_OPTION_CASE_FOLDING, false); xml_set_element_handler($parser_object,"startElement","endElement"); xml_set_character_data_handler($parser_object, "contentHandler"); doParse($parser_object); ?>

Заключение

PHP5 предлагает большое разнообразие методов парсинга. Парсинг при помощи DOM, который теперь полностью совместим со стандартом W3C, — знакомый вариант, подходящий для сложных, но относительно компактных документов. SimpleXML — это способ работы с простыми и не слишком объемными документами, а потоковый парсер XMLReader, который легче и быстрее, чем SAX, предпочтителен для очень больших документов.

→ Для чего нужны XML-парсеры и чем они могут быть полезны

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

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

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

Но универсальность текстового формата XML оборачивается вполне очевидным неудобством - перед извлечением данных из документа надо как следует помучиться с синтаксическим анализом текста и определением его структуры. Реализация всех необходимых процедур вручную - весьма нетривиальное занятие, и потребует немалых усилий. Одним из стандартных механизмов, позволяющих упростить жизнь разработчиков, и являются парсеры.

Что же это такое? XML-парсер - это программа, предназначенная для анализа содержимого текстового документа, который соответствует спецификации XML. Ей достается вся "чёрная" работа: получение общих сведений о документе, анализ текста, поиск в нем служебных конструкций (элементов, атрибутов, сущностей и т.п.), проверка на соблюдение синтаксических правил, а также предоставление интерфейса для доступа к документу. В результате аккуратно извлеченные данные, будут переданы пользовательскому приложению, которое может вообще ничего не знать о том, что такое XML.

Парсер может быть выполнен в виде отдельного программного модуля или ActiveX-компонента, может подключаться к приложению через специальные библиотеки классов на этапе компиляции или выполнения. Парсеры делятся на верифицирующие (validating) и неверифицирующие (non-validating). Первые могут проверять структуру документа на основе DTD или схем данных, вторые же об этом не заботятся - и потому имеют, как правило, меньший размер. Многие из современных парсеров "нагружены" многочисленными дополнительными возможностями (расширенная обработка ошибок, добавление и редактирование данных), что делает их более удобными в работе, хотя и повышает размер программ. Практически все распространенные парсеры поддерживают также ряд важных XML-стандартов (XSLT, схемы данных, Name spaces, XPath и т.д.) - или же поставляются в связке с парсерами других производных от него языков.

Если вы осознали полезность парсера для XML, то пора начинать практические опыты. Где же их взять? Особых проблем с поиском соответствующего софта возникнуть не должно: в Интернете полно свободно распространяемых парсеров, написанных на всевозможных языках программирования, работающих на всех платформах и имеющих самые разные характеристики и предназначение.

Наиболее распространенным и известным является парсер Expat, написанный Джеймсом Кларком - одним из создателей спецификации XML. Он реализован на языке программирования C++ и распространяется вместе с исходным кодом. Кстати, поддержка этого языка разметки в таких известных средах, как РНР и Perl, реализована именно на его основе. Другой распространенный парсер - Xerces, есть в Apache XML Project (реализован на языках Java и C++). Можно найти много парсеров для C++, Perl и Python. Основная их масса написана на Java, и годится для любой платформы, знакомой с Java. Не остались в стороне и лидеры рынка (Microsoft, Oracle, Sun), всегда отличающиеся масштабностью и монументальностью. Они выпустили более "тяжеловесные" и функциональные пакеты, которые содержат, помимо собственно парсеров, множество дополнительных утилит, облегчающих жизнь разработчиков.

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