Intereting Posts
Задержка при заполнении данных session.upload_progress Как определить поддержку X-Accel-Redirect (Nginx) / X-Sendfile (Apache) в PHP? PDO, MySQL – Как вернуть массив из функции? PHP: Какой самый быстрый и простой способ получить последний элемент массива? При использовании уникальной буквенно-цифровой строки для короткого URL-адреса лучше ли хранить созданную строку в базе данных или кодировать / декодировать «на лету»? Создание текстового файла для загрузки на лету Инструменты для рефакторинга PHP-кода filter_var_array () многомерный массив PHP: отправить ical с помощью php mailer Добавьте класс CSS ко всем изображениям на странице с шириной менее 480 пикселей с помощью DomDocument Создание уникальной строки в PHP для базы данных Mysql Сортировка списка файлов по дате PHP получает значение опции выбора Как получить доступ к вложенным массивам в виде laravel? Laravel 4 Как назначить новые значения сохраненным данным в yii 1?

Анализ через неизвестный XML

Я сделал простой инструмент, который позволяет заполнить поле ввода URL-адресом для XML-файла. Предполагается показать все узлы, чтобы пользователь мог сопоставить их с полями базы данных, которые я работаю для XML-файла с двумя «первичными» узлами. Пример файла XML:

<foods> <food> <name>ravioli</name> <recipe>food.com/ravioli</recipe> <time>10 minutes</time> </food> <food> <name>ravioli</name> <recipe>food.com/ravioli</recipe> <time>10 minutes</time> </food> </foods> 

Это возвращает мне список, в котором говорится:

name recipe time

Проблема в том, что кто-то хочет использовать XML-файл, который не имеет 2 «первичных» узлов. Например, отсутствует узел <food> . В этом случае он не сможет показать результат, потому что мой PHP-код ожидает 2 вместо 1 первичного.

Мой код выглядит следующим образом:

 // Fetch the XML from the URL if (!$xml = simplexml_load_file($_GET['url'])) { // The XML file could not be reached echo 'Error loading XML. Please check the URL.'; } else { // Parse through the XML and fetch the nodes $child = $xml->children(); foreach($child->children() as $key => $value) { echo $key."<br>"; } } 

Есть ли способ получить узлы, которые я хочу получить из любого XML-файла, независимо от количества родительских узлов?

Вы можете запрашивать данные из XML DOM, используя Xpath. Он доступен на PHP с использованием метода DOMXpath :: evaluation (). Второй аргумент – это контекст, поэтому вы можете относиться к другому узлу. Преобразование его в список записей (для базы данных, csv, …). потребуется несколько шагов. Начиная с некоторого бутстрапа:

 $xml = <<<'XML' <foods> <food> <name>ravioli 1</name> <recipe>food.com/ravioli-1</recipe> <time unit="minutes">10</time> </food> <food> <name>ravioli 2</name> <recipe>food.com/ravioli-2</recipe> <time unit="minutes">11</time> </food> </foods> XML; $dom = new DOMDocument(); $dom->loadXml($xml); $xpath = new DOMXpath($dom); 

Сначала нам нужно определить, какой элемент xml определяет запись, затем какие элементы определяют поля.

Итак, давайте создадим списки возможных путей записи и путей полей:

 $paths = []; $leafs = []; foreach ($xpath->evaluate('//*|//@*') as $node) { $isPath = $xpath->evaluate('count(@*|*) > 0', $node); $isLeaf = !($xpath->evaluate('count(*) > 0', $node)); $path = ''; foreach ($xpath->evaluate('ancestor::*', $node) as $parent) { $path .= '/'.$parent->nodeName; } $path .= '/'.($node instanceOf DOMAttr ? '@' : '').$node->nodeName; if ($isLeaf) { $leafs[$path] = TRUE; } if ($isPath) { $paths[$path] = TRUE; } } $paths = array_keys($paths); $leafs = array_keys($leafs); var_dump($paths, $leafs); 

Вывод:

 array(3) { [0] => string(6) "/foods" [1] => string(11) "/foods/food" [2] => string(16) "/foods/food/time" } array(4) { [0] => string(16) "/foods/food/name" [1] => string(18) "/foods/food/recipe" [2] => string(16) "/foods/food/time" [3] => string(22) "/foods/food/time/@unit" } 

Затем покажите возможные пути записи для пользователя. Пользователь должен выбрать один. Зная путь записи, создайте список возможных путей поля из массива leafs:

 $path = '/foods/food'; $fieldLeafs = []; $pathLength = strlen($path) + 1; foreach ($leafs as $leaf) { if (0 === strpos($leaf, $path.'/')) { $fieldLeafs[] = substr($leaf, $pathLength); } } var_dump($fieldLeafs); 

Вывод:

 array(4) { [0] => string(4) "name" [1] => string(6) "recipe" [2] => string(4) "time" [3] => string(10) "time/@unit" } 

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

 $fieldDefinition = [ 'title' => 'name', 'url' => 'recipe', 'needed_time' => 'time', 'time_unit' => 'time/@unit' ]; 

Теперь используйте путь и сопоставление для создания массива записей:

 $result = []; foreach ($xpath->evaluate($path) as $node) { $record = []; foreach ($fieldDefinition as $field => $expression) { $record[$field] = $xpath->evaluate( 'string('.$expression.')', $node ); } $result[] = $record; } var_dump($result); 

Вывод:

 array(2) { [0] => array(4) { 'title' => string(9) "ravioli 1" 'url' => string(18) "food.com/ravioli-1" 'needed_time' => string(2) "10" 'time_unit' => string(7) "minutes" } [1] => array(4) { 'title' => string(9) "ravioli 2" 'url' => string(18) "food.com/ravioli-2" 'needed_time' => string(2) "11" 'time_unit' => string(7) "minutes" } } 

Полный пример можно найти по адресу: https://eval.in/118012

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