Php разбор xml файла

Работаем с 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 имеет свои особенности и некоторые вещи далеко не очевидны.

Читайте также:  Java throw from catch

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.

Читайте также:  Find and replace all python

Из названий методов очевидно их назначение. Не очевидные были рассмотрены выше.

Как установить?

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 приводит документ к объекту, а у объекта нет такого понятия как порядок следования элементов.

Источник

Оцените статью