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

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

Предлагаю парсить созданный нами в прошлой статье документ, а данные оттуда просто вывести в браузер. Привожу код скрипта:

$dom = new domDocument("1.0", "utf-8"); // Создаём XML-документ версии 1.0 с кодировкой utf-8
$dom->load("users.xml"); // Загружаем XML-документ из файла в объект DOM
$root = $dom->documentElement; // Получаем корневой элемент
$childs = $root->childNodes; // Получаем дочерние элементы у корневого элемента
/* Перебираем полученные элементы */
for ($i = 0; $i < $childs->length; $i++) {
$user = $childs->item($i); // Получаем следующий элемент из NodeList
$lp = $user->childNodes; // Получаем дочерние элементы у узла "user"
$id = $user->getAttribute("id"); // Получаем значение атрибута "id" у узла "user"
$login = $lp->item(0)->nodeValue; // Получаем значение узла "login"
$password = $lp->item(1)->nodeValue; // Получаем значение узла "password"
/* Выводим полученные данные */
echo "ID: $id
";
echo "Логин: $login
";
echo "Пароль: $password
";
echo "-----------------------
";
}
?>

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

Парсинг 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 прочтите документацию .


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

Many examples in this reference require an XML string. Instead of repeating this string in every example, we put it into a file which we include in each example. This included file is shown in the following example section. Alternatively, you could create an XML document and read it with simplexml_load_file() .

Example #1 Include file example.php with XML string

$xmlstr = <<


PHP: Behind the Parser


Ms. Coder
Onlivia Actora


Mr. Coder
El ActÓr


So, this language. It"s like, a programming language. Or is it a
scripting language? All is revealed in this thrilling horror spoof
of a documentary.




7
5


XML;
?>

The simplicity of SimpleXML appears most clearly when one extracts a string or number from a basic XML document.

Example #2 Getting

include "example.php" ;

echo $movies -> movie [ 0 ]-> plot ;
?>

So, this language. It"s like, a programming language. Or is it a scripting language? All is revealed in this thrilling horror spoof of a documentary.

Accessing elements within an XML document that contain characters not permitted under PHP"s naming convention (e.g. the hyphen) can be accomplished by encapsulating the element name within braces and the apostrophe.

Example #3 Getting

include "example.php" ;

echo $movies -> movie ->{ "great-lines" }-> line ;
?>

The above example will output:

PHP solves all my web problems

Example #4 Accessing non-unique elements in SimpleXML

When multiple instances of an element exist as children of a single parent element, normal iteration techniques apply.

include "example.php" ;

$movies = new SimpleXMLElement ($xmlstr );

/* For each node, we echo a separate . */
foreach ($movies -> movie -> characters -> character as $character ) {
echo $character -> name , " played by " , $character -> actor , PHP_EOL ;
}

?>

The above example will output:

Properties ($movies->movie in previous example) are not arrays. They are iterable and accessible objects.

Example #5 Using attributes

So far, we have only covered the work of reading element names and their values. SimpleXML can also access element attributes. Access attributes of an element just as you would elements of an array .

include "example.php" ;

$movies = new SimpleXMLElement ($xmlstr );

/* Access the nodes of the first movie.
* Output the rating scale, too. */
foreach ($movies -> movie [ 0 ]-> rating as $rating ) {
switch((string) $rating [ "type" ]) { // Get attributes as element indices
case "thumbs" :
echo $rating , " thumbs up" ;
break;
case "stars" :
echo $rating , " stars" ;
break;
}
}
?>

The above example will output:

7 thumbs up5 stars

Example #6 Comparing Elements and Attributes with Text

To compare an element or attribute with a string or pass it into a function that requires a string, you must cast it to a string using (string) . Otherwise, PHP treats the element as an object.

include "example.php" ;

$movies = new SimpleXMLElement ($xmlstr );

if ((string) $movies -> movie -> title == "PHP: Behind the Parser" ) {
print "My favorite movie." ;
}

echo htmlentities ((string) $movies -> movie -> title );
?>

The above example will output:

My favorite movie.PHP: Behind the Parser

Example #7 Comparing Two Elements

Two SimpleXMLElements are considered different even if they point to the same element since PHP 5.2.0.

include "example.php" ;

$movies1 = new SimpleXMLElement ($xmlstr );
$movies2 = new SimpleXMLElement ($xmlstr );
var_dump ($movies1 == $movies2 ); // false since PHP 5.2.0
?>

The above example will output:

Example #8 Using XPath

SimpleXML includes built-in XPath support. To find all elements:

include "example.php" ;

$movies = new SimpleXMLElement ($xmlstr );

foreach ($movies -> xpath ("//character" ) as $character ) {
echo $character -> name , " played by " , $character -> actor , PHP_EOL ;
}
?>

"// " serves as a wildcard. To specify absolute paths, omit one of the slashes.

The above example will output:

Ms. Coder played by Onlivia Actora Mr. Coder played by El ActÓr

Example #9 Setting values

Data in SimpleXML doesn"t have to be constant. The object allows for manipulation of all of its elements.

include "example.php" ;
$movies = new SimpleXMLElement ($xmlstr );

$movies -> movie [ 0 ]-> characters -> character [ 0 ]-> name = "Miss Coder" ;

echo $movies -> asXML ();
?>

The above example will output:

PHP: Behind the Parser Miss Coder Onlivia Actora Mr. Coder El ActÓr PHP solves all my web problems 7 5

Example #10 Adding elements and attributes

Since PHP 5.1.3, SimpleXML has had the ability to easily add children and attributes.

include "example.php" ;
$movies = new SimpleXMLElement ($xmlstr );

$character = $movies -> movie [ 0 ]-> characters -> addChild ("character" );
$character -> addChild ("name" , "Mr. Parser" );
$character -> addChild ("actor" , "John Doe" );

$rating = $movies -> movie [ 0 ]-> addChild ("rating" , "PG" );
$rating -> addAttribute ("type" , "mpaa" );

echo $movies -> asXML ();
?>

The above example will output:

PHP: Behind the Parser Ms. Coder Onlivia Actora Mr. Coder El ActÓr Mr. ParserJohn Doe So, this language. It"s like, a programming language. Or is it a scripting language? All is revealed in this thrilling horror spoof of a documentary. PHP solves all my web problems 7 5 PG

Example #11 DOM Interoperability

PHP has a mechanism to convert XML nodes between SimpleXML and DOM formats. This example shows how one might change a DOM element to SimpleXML.

$dom = new DOMDocument ;
$dom -> loadXML ("blah" );
if (! $dom ) {
echo "Error while parsing the document" ;
exit;
}

$books = simplexml_import_dom ($dom );

echo $books -> book [ 0 ]-> title ;
?>

The above example will output:

4 years ago

There is a common "trick" often proposed to convert a SimpleXML object to an array, by running it through json_encode() and then json_decode(). I"d like to explain why this is a bad idea.

Most simply, because the whole point of SimpleXML is to be easier to use and more powerful than a plain array. For instance, you can write bar -> baz [ "bing" ] ?> and it means the same thing as bar [ 0 ]-> baz [ 0 ][ "bing" ] ?> , regardless of how many bar or baz elements there are in the XML; and if you write bar [ 0 ]-> baz [ 0 ] ?> you get all the string content of that node - including CDATA sections - regardless of whether it also has child elements or attributes. You also have access to namespace information, the ability to make simple edits to the XML, and even the ability to "import" into a DOM object, for much more powerful manipulation. All of this is lost by turning the object into an array rather than reading understanding the examples on this page.

Additionally, because it is not designed for this purpose, the conversion to JSON and back will actually lose information in some situations. For instance, any elements or attributes in a namespace will simply be discarded, and any text content will be discarded if an element also has children or attributes. Sometimes, this won"t matter, but if you get in the habit of converting everything to arrays, it"s going to sting you eventually.

Of course, you could write a smarter conversion, which didn"t have these limitations, but at that point, you are getting no value out of SimpleXML at all, and should just use the lower level XML Parser functions, or the XMLReader class, to create your structure. You still won"t have the extra convenience functionality of SimpleXML, but that"s your loss.

9 years ago

If you need to output valid xml in your response, don"t forget to set your header content type to xml in addition to echoing out the result of asXML():

$xml = simplexml_load_file ("..." );
...
... xml stuff
...

//output xml in your response:
header ("Content-Type: text/xml" );
echo $xml -> asXML ();
?>

1 year ago

If your xml string contains booleans encoded with "0" and "1", you will run into problems when you cast the element directly to bool:

$xmlstr = <<

1
0

XML;
$values = new SimpleXMLElement($xmlstr);
$truevalue = (bool)$values->truevalue; // true
$falsevalue = (bool)$values->falsevalue; // also true!!!

Instead you need to cast to string or int first:

$truevalue = (bool)(int)$values->truevalue; // true
$falsevalue = (bool)(int)$values->falsevalue; // false

9 years ago

From the README file:

SimpleXML is meant to be an easy way to access XML data.

SimpleXML objects follow four basic rules:

1) properties denote element iterators
2) numeric indices denote elements
3) non numeric indices denote attributes
4) string conversion allows to access TEXT data

When iterating properties then the extension always iterates over
all nodes with that element name. Thus method children() must be
called to iterate over subnodes. But also doing the following:
foreach ($obj->node_name as $elem) {
// do something with $elem
}
always results in iteration of "node_name" elements. So no further
check is needed to distinguish the number of nodes of that type.

When an elements TEXT data is being accessed through a property
then the result does not include the TEXT data of subelements.

Known issues
============

Due to engine problems it is currently not possible to access
a subelement by index 0: $object->property.

8 years ago

A quick tip on xpath queries and default namespaces. It looks like the XML-system behind SimpleXML has the same workings as I believe the XML-system .NET uses: when one needs to address something in the default namespace, one will have to declare the namespace using registerXPathNamespace and then use its prefix to address the otherwise in the default namespace living element.

$string = <<

Forty What?
Joe
Jane

I know that"s the answer -- but what"s the question?


XML;

$xml = simplexml_load_string ($string );
$xml -> registerXPathNamespace ("def" , "http://www.w3.org/2005/Atom " );

$nodes = $xml -> xpath ("//def:document/def:title" );

?>

8 years ago

Using stuff like: is_object($xml->module->admin) to check if there actually is a node called "admin", doesn"t seem to work as expected, since simplexml always returns an object- in that case an empty one - even if a particular node does not exist.
For me good old empty() function seems to work just fine in such cases.

9 years ago

While SimpleXMLElement claims to be iterable, it does not seem to implement the standard Iterator interface functions like::next and::reset properly. Therefore while foreach() works, functions like next(), current(), or each() don"t seem to work as you would expect -- the pointer never seems to move or keeps getting reset.

5 years ago

Если кодировка XML-документа отличается от UTF-8, объявление кодировки должно следовать сразу после version="..." и перед standalone="...". Это требование стандарта XML.

If encoding XML-document differs from UTF-8. Encoding declaration should follow immediately after the version = "..." and before standalone = "...". This requirement is standard XML.


Ok

Русский язык. Russian language
Fatal error: Uncaught exception "Exception" with message "String could not be parsed as XML" in...

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

До этого XML-документ парсился с помощью функций, которые заимствованы из PHP 4-й версии. Однако PHP5 подарил миру очень удобную вещь под названием SimpleXML. О том, как же с ним работать, и пойдёт речь сегодня.

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

Теперь мы можем работать!

Для того, чтобы обработать документ, используем функцию simplexml_load_file(). В качестве параметра ей передается адрес файла в формате eXtended Markup Language (XML — Ваш К.О.).

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

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

Для примера я возьму простенькую конструкцию отсюда :


>
>
> PHP: Появление Парсера>
>
>
> Ms. Coder>
> Onlivia Actora>
>
>
> Mr. Coder>
> El ActÓ r>
>
> > Mr. Parser> > John Doe> > >
>
Таким образом, это язык. Это все равно язык программирования. Или
это скриптовый язык? Все раскрывается в этом документальном фильме,
похожем на фильм ужасов.
>
>
> PHP решает все мои задачи в web>
>
7>
5>
PG> >
>

Пусть это будет файл export.xml, который лежит прямо в корне моего сервера вместе с обрабатывающим его скриптом.
Массив строится в соответствии со структурой DOM-элементов в XML-документе. Обработка начинается от корня. Для того, чтобы получить имя Ms. Coder, мы должны выстроить следующий путь: $xml->movies->movie->characters->character->name.
Обращаю внимание, что мы выбираем конкретное значение. Отсюда и берется запись такого рода character — не забываем, что мы работаем с массивом!

Как и любой массив, наши данные можно обработать при помощи цикла foreach. Код будет такой:

$xml = simplexml_load_file ("export.xml" ) ; //выгрузили файл
$ttl = $xml -> movies -> movie -> title ; //получили заголовок. он один, так что или другое значение ставить не надо

foreach ($xml -> movies -> movie -> caracters as $crc ) // а теперь поработаем в динамике
{
//выведем имена героев
$name = $crc -> caracter -> name ;
echo ("$name
"
) ;
}

Такой код положит в переменную $ttl текст «PHP: Появление Парсера», а затем выведет построчно на экран имена героев
Ms. Coder, Mr. Coder, Mr. Parser.

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, предпочтителен для очень больших документов.