- Работаем с XML как с массивом, на PHP
- XMLReader
- SimpleXMLElement
- Converter
- XmlNavigator
- Все методы класса XmlNavigator
- Как установить?
- Заключение
- Эпилог
- PHP XML Reader Example
- Класс XMLReader
- Обзор классов
- Свойства
- Предопределенные константы
- Типы узлов XMLReader
- Опции анализатора XMLReader
- Saved searches
- Use saved searches to filter your results more quickly
- tmitry/xmlreader-extension
- Name already in use
- Sign In Required
- Launching GitHub Desktop
- Launching GitHub Desktop
- Launching Xcode
- Launching Visual Studio Code
- Latest commit
- Git stats
- Files
- README.md
- About
Работаем с XML как с массивом, на PHP
Всем привет. Хочу поделиться своим опытом в парсинге XML, хочу рассказать об инструменте который мне в этом помогает.
XML ещё жив и иногда его приходиться парсить. Особенно если вы работаете со СМЭВ (привет всем ребятам для которых «ФОИВ» не пустой звук 🙂 ).
Цели у такого парсинга могут быть самые разные, от банального ответа на вопрос какое пространство имён используется в xml-документе, до необходимости получить структурированное представление для документа вцелом.
Инструмент для каждой цели будет свой. Пространство имён можно найти поиском подстроки или регулярным выражением. Что бы сделать из xml-документа структурированное представление (DTO) — придётся писать парсер.
Для работы с XML в PHP есть пара встроенных классов. Это XMLReader и SimpleXMLElement.
XMLReader
С помощью XMLReader парсинг будет выглядеть примерно так :
$reader = (new XMLReader()); $reader->XML($content); while ($reader->read()) < $this->parse($reader); >
Внутри метода parse(XMLReader $xml) будут бесконечные:
$name = $xml->name; $value = $xml->expand()->textContent; $attrVal = $xml->getAttribute('attribute'); $isElem = $xml->nodeType === XMLReader::ELEMENT;
Для небольших документов или когда нам из всего документа надо только пару элементов, это приемлемо, на больших объёмах — начинает в глазах рябить от однообразного кода, плюс совесть грызёт за оверхэд от перебора всех элементов документа.
SimpleXMLElement
Провести анализ только нужных элементов помогает SimpleXMLElement. Этот класс из XML-документа делает объект, у которого все элементы и атрибуты становятся свойствами, то есть появляется возможность работать только с определёнными элементами, а не со всеми подряд, пример:
$document = new SimpleXMLElement($content); /* имя корневого элемента */ $name = $document->getName(); /* получить произвольный элемент */ $primary = $document ->Message ->ResponseContent ->content ->MessagePrimaryContent ?? null; /* получить элементы определённого пространства имён */ $attachment = $primary ->children( 'urn://x-artefacts-fns-zpvipegr/root/750-08/4.0.1' ) ->xpath('tns:Вложения/fnst:Вложение')[0]; /* получить значение элемента */ $fileName = $attachment ->xpath('//fnst:ИмяФайла')[0] ->__toString();
Удобно, да не совсем. Если имя элемента на кириллице, то обратиться к нему через свойство не получиться, придётся использовать SimpleXMLElement::xpath(). С множественными значениями так же приходиться работать через SimpleXMLElement::xpath(). Кроме того SimpleXMLElement имеет свои особенности и некоторые вещи далеко не очевидны.
Converter
Есть способ проще. Достаточно XML-документ привести к массиву. В работе с массивами нет ни каких подводных камней. Массив из XML делается в пару строчек кода:
$xml= ccc 0000 XML; $fabric = (new NavigatorFabric())->setXml($xml); $converter = $fabric->makeConverter(); $arrayRepresentationOfXml = $converter->toArray();
Каждый XML-элемент будет представлен массивом, состоящим в свою очередь, из трёх других массивов.
- массив с индексом ‘*value’ содержит значение элемента,
- ‘*attributes’ — атрибуты элемента,
- ‘*elements’ — вложенные элементы.
/* 'b' => array ( '*value' => '0000', '*attributes' => array ( 'attr4' => '55', ), '*elements' => array ( 'c' => array ( ), ), ), */
Если элемент множественный, то есть встречается в документе несколько раз подряд, то все его вхождения будут в массиве с индексом ‘*multiple’.
$xml= first occurrence second occurrence XML; /* 'doc' => array ( 'qwe' => array ( '*multiple' => array ( 0 => array ( '*value' => 'first occurrence', ), 1 => array ( '*value' => 'second occurrence', ) ) ) ) */
XmlNavigator
Если от работы с XML-документов как с массивом, у вас в глазах рябит от квадратных скобочек, то XmlNavigator — это ваш вариант, создаётся так же в две строки кода.
/* документ */ $xml = 666 element value 0 XML; $fabric = (new NavigatorFabric())->setXml($xml); $navigator = $fabric->makeNavigator();
XmlNavigator делает, то же самое что и Converter, но предоставляет API, и с документом мы работаем как с объёктом.
Имя элемента, метод name()
/* Имя элемента */ echo $navigator->name(); /* doc */
Значение элемента, метод value()
/* Значение элемента */ echo $navigator->value(); /* 666 */
Список атрибутов, метод attribs()
/* get list of attributes */ echo var_export($navigator->attribs(), true); /* array ( 0 => 'attrib', 1 => 'option', ) */
Значение атрибута, метод get()
/* get attribute value */ echo $navigator->get('attrib'); /* a */
Список вложенных элементов, метод elements()
/* Список вложенных элементов */ echo var_export($navigator->elements(), true); /* array ( 0 => 'base', 1 => 'valuable', 2 => 'complex', ) */
Получить вложенный элемент, метод pull()
/* Получить вложенный элемент */ $nested = $navigator->pull('complex'); echo $nested->name(); /* complex */ echo var_export($nested->elements(), true); /* array ( 0 => 'a', 1 => 'different', 2 => 'b', 3 => 'c', ) */
Перебрать все вхождения множественного элемента, метод next()
/* Получить вложенный элемент вложенного элемента */ $multiple = $navigator->pull('complex')->pull('b'); /* Перебрать все вхождения множественного элемента */ foreach ($multiple->next() as $index => $instance) < echo " name()>[$index]" . " => get('val')>;"; > /* b[0] => x; b[1] => y; b[2] => z; */
Все методы класса XmlNavigator
Класс XmlNavigator реализует интерфейс IXmlNavigator.
Из названий методов очевидно их назначение. Не очевидные были рассмотрены выше.
Как установить?
composer require sbwerewolf/xml-navigator
Заключение
В работе приходиться использовать сначала SimpleXMLElement — с его помощью из всего документа получаем необходимый элемент, и уже с этим элементом работаем через XmlNavigator.
$document = new SimpleXMLElement($content); $primary = $document ->Message ->ResponseContent ->content ->MessagePrimaryContent; $attachment = $primary ->children( 'urn://x-artefacts-fns-zpvipegr/root/750-08/4.0.1' ) ->xpath('tns:Вложения')[0]; $fabric = (new NavigatorFabric())->setSimpleXmlElement($attachment); $navigator = $fabric->makeNavigator();
Желаю вам приятного использования.
Эпилог
Конечно у вас могут быть свои альтернативы для работы с XML. Предлагаю поделиться в комментариях.
Конечно, не могу сказать, что XmlNavigator поможет с любым XML — не проверял, но с обычными документами, без хитростей в схеме документа, проблем не было.
Если вам важен порядок следования элементов, то придётся пользоваться XMLReader. Потому что SimpleXMLElement приводит документ к объекту, а у объекта нет такого понятия как порядок следования элементов.
PHP XML Reader Example
Today, In this tutorial we are going to learn how to parse an large XML file using XMLReader Class. The XMLReader extension is an XML Pull parser. The reader acts as a cursor going forward on the document stream and stopping at each node on the way.
The XMLReader extension was initially a PECL extension for PHP 5. It was later moved to the PHP source bundled as of PHP 5.1.0, and later enabled by default as of PHP 5.1.2
- It is faster since it is not loading the whole XML into memory.
- It can parse large and high complex XML document having more sub-trees.
- Retrieving portion of XML document based on current node.
- Getting attributes based on index, name or namespace.
- Parsing elements based on attribute’s index, name or namespace.
- Validating XML document
Lets see the example where we are going to parse an xml tag from the external source file. Here we are using XMLReader to get to each node, then use SimpleXML to access them. This way, you keep the memory usage low because you’re treating one node at a time and you still leverage SimpleXML’s ease of use.
xml version="1.0" encoding="UTF-8"?> joindate="12/11/2007"> Sumit Kumar Pradhan 29377493 34 333339 joindate="01/11/2017"> Amit Kumar 15454545 23 256454
php $reader = new XMLReader(); $doc = new DOMDocument; // read file external xml file. if (!$reader->open("user-data.xml")) die("Failed to open 'user-data.xml'"); > // reading xml data. while($reader->read()) if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'employee') $node = simplexml_import_dom($doc->importNode($reader->expand(), true)); echo ""
; //get employee join date echo $address = $reader->getAttribute('joindate')."
"; // get username echo $node->username."
"; // get employee id echo $node->empid."
"; // get employee age echo $node->age."
"; //get employee salary echo "
"; > > $reader->close(); ?>
Класс XMLReader
Расширение XMLReader — синтаксический анализатор XML. Класс-читатель выступает в качестве курсора, следует по потоку документа и останавливается на каждом узле на этом пути.
Обзор классов
Свойства
Количество атрибутов в узле
Глубина узла в дереве, начиная с 0
Показывает, есть ли у узла атрибуты
Показывает, имеет ли узел текстовое значение
Показывает, является ли атрибутом по умолчанию из DTD
Показывает, является ли узел пустым тегом
Полностью определенное имя узла
URI пространства имён связанный с узлом
Префикс пространства имён связанный с узлом
Контекст xml:lang, в котором находится узел
Предопределенные константы
Типы узлов XMLReader
XMLReader::ELEMENT
XMLReader::ATTRIBUTE
XMLReader::TEXT
XMLReader::CDATA
XMLReader::ENTITY_REF
XMLReader::ENTITY
XMLReader::PI
Узел инструкций обработки
XMLReader::COMMENT
XMLReader::DOC
XMLReader::DOC_TYPE
XMLReader::DOC_FRAGMENT
XMLReader::NOTATION
XMLReader::WHITESPACE
XMLReader::SIGNIFICANT_WHITESPACE
Существенный пробельный узел
XMLReader::END_ELEMENT
XMLReader::END_ENTITY
XMLReader::XML_DECLARATION
Опции анализатора XMLReader
Загружать DTD, но не проверять
XMLReader::DEFAULTATTRS
Загружать DTD и атрибуты по умолчанию, но не проверять
XMLReader::VALIDATE
Загружать DTD и проверять при разборе
XMLReader::SUBST_ENTITIES
Заменять объекты и разворачивать ссылки
Saved searches
Use saved searches to filter your results more quickly
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
XMLReader for PHP extension
tmitry/xmlreader-extension
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Add the tmitry/xmlreader-extension package to your require section in the composer.json file.
$ composer require tmitry/xmlreader-extension
use tmitry\XMLReaderExtension\XMLParser;
You can use existing instance of XMLReader
$xmlReader = new XMLReader(); $xmlReader->open($fileName); // . some actions with instance of XMLReader $parser = new XMLParser($xmlReader);
$parser = new XMLParser(); $parser->open($fileName);
Use extended or standart interface of XMLReader
// extended interface if ($parser->moveTo('root/products') && $parser->moveIn()) < do < if ('root/products/product' == $parser->getPath()) < // standart XMLReader interface echo $parser->readOuterXml(); > > while ($parser->moveToNextSibling()); >
About
XMLReader for PHP extension