Web

Как использовать XMLReader в PHP

У меня есть следующий XML-файл, файл довольно большой, и мне не удалось заставить simplexml открыть и прочитать файл, поэтому я безуспешно пытаюсь использовать XMLReader в php:

<?xml version="1.0" encoding="ISO-8859-1"?>

<products>

    <last_updated>2019-11-30 13:52:40</last_updated>

    <product>

        <element_1>foo</element_1>

        <element_2>foo</element_2>

        <element_3>foo</element_3>

        <element_4>foo</element_4>

    </product>

    <product>

        <element_1>bar</element_1>

        <element_2>bar</element_2>

        <element_3>bar</element_3>

        <element_4>bar</element_4>

    </product>

</products>

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

 

 Ответ 1

Все зависит от того, насколько велики затраты на работу, но я предполагаю, что вы пытаетесь последовательно обрабатывать каждый узел <product/>. Для этого проще всего использовать XMLReader, чтобы добраться до каждого узла, а затем использовать SimpleXML для доступа к ним. Таким образом, вы сохраните низкое потребление памяти, поскольку вы обрабатываете по одному узлу за раз, и вы все еще используете простоту использования SimpleXML. Например:

$z = new XMLReader;

$z->open('data.xml');

$doc = new DOMDocument;

// переходим к первому узлу <product />

while ($z->read() && $z->name !== 'product');

// теперь, когда мы находимся на нужной глубине, переходим к следующему <product/> до конца дерева

while ($z->name === 'product') {

    // любой из них должен работать

    //$node = new SimpleXMLElement($z->readOuterXML());

    $node = simplexml_import_dom($doc->importNode($z->expand(), true));

    // теперь вы можете использовать $node, не заморачиваясь с парсингом

    var_dump($node->element_1);

    // перейти к следующему узлу <product />

    $z->next('product');

}

Краткий обзор плюсов и минусов различных подходов:

Только XMLReader

  1. Плюсы: быстрый способ, использует мало памяти.

  2. Минусы: слишком сложно писать и отлаживать, требуется много пользовательского кода, чтобы сделать что-нибудь полезное. Пользовательский код медленный и склонен к ошибкам. Кроме того, вам придется поддерживать больше строк кода.

XMLReader + SimpleXML

  1. Плюсы: не использует много памяти (только память, необходимую для обработки одного узла), а SimpleXML, как следует из названия, очень прост в использовании.

  2. Минусы: создание объекта SimpleXMLElement для каждого узла происходит не очень быстро. Чтобы понять, является ли это проблемой для вас, нужно провести сравнительный анализ. Однако даже скромная машина сможет обрабатывать тысячу узлов в секунду.

XMLReader + DOM

  1. Плюсы: использует примерно столько же памяти, сколько SimpleXML, а XMLReader::expand() быстрее, чем создание нового элемента SimpleXMLElement. Хотелось бы, чтобы можно было использовать simplexml_import_dom(), но, похоже, в этом случае он не работает.

  2. Минусы: DOM неудобен в работе. Это нечто среднее между XMLReader и SimpleXML. Не так сложно и неудобно, как XMLReader, но до работы с SimpleXML еще очень далеко.

Мой совет: напишите прототип с SimpleXML, посмотрите, подойдет ли он вам. Если производительность имеет первостепенное значение, попробуйте DOM. Держитесь как можно дальше от XMLReader. Помните, что чем больше кода вы пишете, тем выше вероятность того, что вы внедрите ошибки или допустите регресс производительности.

 

Ответ 2

Для xml, отформатированного с атрибутами...

data.xml:

<building_data>

<building address="some address" lat="28.902914" lng="-71.007235" />

<building address="some address" lat="48.892342" lng="-75.0423423" />

<building address="some address" lat="58.929753" lng="-79.1236987" />

</building_data>

 

php code:

$reader = new XMLReader();

if (!$reader->open("data.xml")) {

    die("Failed to open 'data.xml'");

}

while($reader->read()) {

  if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'building') {

    $address = $reader->getAttribute('address');

    $latitude = $reader->getAttribute('lat');

    $longitude = $reader->getAttribute('lng');

}

$reader->close();



Ответ 3

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

Я считаю, что самый простой способ использования XMLReader это знать, из каких тегов необходимо получать информацию, и использовать их. Если вы знаете структуру XML и в нем много уникальных тегов, я считаю, что использовать первый случай проще всего. Случаи 2 и 3 просто показывают, как это можно сделать для более сложных тегов. Самое важное, что нужно помнить при выполнении парсинга на основе тегов, как в этом случае, это использовать if ($myXML->nodeType == XMLReader::ELEMENT) {..., который проверяет, что мы имеем дело только с открывающимися узлами, а не с пробелами, закрывающимися узлами или чем-либо еще.

function parseMyXML ($xml) { //передать строку XML

    $myXML = new XMLReader();

    $myXML->xml($xml);

    while ($myXML->read()) { //начать чтение.

        if ($myXML->nodeType == XMLReader::ELEMENT) { //только открывающие теги.

            $tag = $myXML->name; //заставить $tag содержать имя тега

            switch ($tag) {

                case 'Tag1': //Этот тег не содержит дочерних элементов, только нужное нам содержимое. И оно уникально.

                    $variable = $myXML->readInnerXML();//теперь переменная содержит содержимое тега1

                    break;

                case 'Tag2': //этот тег содержит дочерние элементы, из которых нам нужен только один.

                    while($myXML->read()) { //так что мы говорим ему продолжать читать

                        if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') { // и когда он находит тег amount...

                            $variable2 = $myXML->readInnerXML();//...поместите его в $variable2.

                            break;

                        }

                    }

                    break;

                case 'Tag3': //tag3 также имеет дочерние элементы, которые не являются уникальными, но на этот раз нам нужны два из них.

                    while($myXML->read()) {

                        if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') {

                            $variable3 = $myXML->readInnerXML();

                            break;

                        } else if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Currency'){

                            $variable4 = $myXML->readInnerXML();

                            break;

                        }

                    }

                    break;

            }

        }

    }

$myXML->close();

}

 

Ответ 4

Simple example:

 public function productsAction() {

    $saveFileName = 'ceneo.xml';

    $filename = $this->path . $saveFileName;

    if(file_exists($filename)) {

    $reader = new XMLReader();

    $reader->open($filename);

    $countElements = 0;

    while($reader->read()) {

        if($reader->nodeType == XMLReader::ELEMENT) {

            $nodeName = $reader->name;

        }

        if($reader->nodeType == XMLReader::TEXT && !empty($nodeName)) {

            switch ($nodeName) {

                case 'id':

                    var_dump($reader->value);

                    break;

            }

        }

        if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'offer') {

            $countElements++;

        }

    }

    $reader->close();

    exit(print('<pre>') . var_dump($countElements));

    }

}

 

Ответ 5

Этот код работает лучше и быстрее для меня:


<html>

<head>

<script>

function showRSS(str) {

  if (str.length==0) {

    document.getElementById("rssOutput").innerHTML="";

    return;

  }

  if (window.XMLHttpRequest) {

    // код для IE7+, Firefox, Chrome, Opera, Safari

    xmlhttp=new XMLHttpRequest();

  } else {  // код для IE6, IE5

    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");

  }

  xmlhttp.onreadystatechange=function() {

    if (this.readyState==4 && this.status==200) {

      document.getElementById("rssOutput").innerHTML=this.responseText;

    }

  }

  xmlhttp.open("GET","getrss.php?q="+str,true);

  xmlhttp.send();

}

</script>

</head>

<body>

 

<form>

<select onchange="showRSS(this.value)">

<option value="">Выбрать RSS-ленту:</option>

<option value="Google">Google News</option>

<option value="ZDN">Новости ZDNet</option>

<option value="job">Работа</option>

</select>

</form>

<br>

<div id="rssOutput">RSS-лента будет указана здесь...</div>

</body>

</html> 

 

**Файл бэкенда**

<?php

//получение параметра q из URL

$q=$_GET["q"];

//выяснить, что было выбрано

if($q=="Google") {

  $xml=("http://news.google.com/news?ned=us&topic=h&output=rss");

} elseif($q=="ZDN") {

  $xml=("https://www.zdnet.com/news/rss.xml");

}elseif($q == "job"){

  $xml=("https://ngcareers.com/feed");

}

$xmlDoc = new DOMDocument();

$xmlDoc->load($xml);

// получить элемент из "<channel>"

$channel=$xmlDoc->getElementsByTagName('channel')->item(0);

$channel_title = $channel->getElementsByTagName('title')

->item(0)->childNodes->item(0)->nodeValue;

$channel_link = $channel->getElementsByTagName('link')

->item(0)->childNodes->item(0)->nodeValue;

$channel_desc = $channel->getElementsByTagName('description')

->item(0)->childNodes->item(0)->nodeValue;

// вывод элемента из "<channel>"

echo("<p><a href='" . $channel_link

  . "'>" . $channel_title . "</a>");

echo("<br>");

echo($channel_desc . "</p>");

 

// получить и вывести элементы "<item>"

$x=$xmlDoc->getElementsByTagName('item');

$count = $x->length;

// print_r( $x->item(0)->getElementsByTagName('title')->item(0)->nodeValue);

// print_r( $x->item(0)->getElementsByTagName('link')->item(0)->nodeValue);

// print_r( $x->item(0)->getElementsByTagName('description')->item(0)->nodeValue);

// return;

for ($i=0; $i <= $count; $i++) {

  // заголовок

  $item_title = $x->item(0)->getElementsByTagName('title')->item(0)->nodeValue;

  //ссылка

  $item_link = $x->item(0)->getElementsByTagName('link')->item(0)->nodeValue;

  //описание

  $item_desc = $x->item(0)->getElementsByTagName('description')->item(0)->nodeValue;

  //категория

  $item_cat = $x->item(0)->getElementsByTagName('category')->item(0)->nodeValue;

  echo ("<p>Title: <a href='" . $item_link

  . "'>" . $item_title . "</a>");

  echo ("<br>");

  echo ("Desc: ".$item_desc);

   echo ("<br>");

  echo ("Category: ".$item_cat . "</p>");

}

?> 

 

Ответ 6

Принятый ответ дал мне хороший старт, но принес больше проблем, чем мне хотелось бы, так что я создал свой вариант:

$xml_reader = new XMLReader;

$xml_reader->open($feed_url);

// перемещение указателя на первый продукт

while ($xml_reader->read() && $xml_reader->name != 'product');

// просмотреть все товары

while ($xml_reader->name == 'product') {

    // загрузите текущий элемент xml в simplexml, и мы готовы к работе!

    $xml = simplexml_load_string($xml_reader->readOuterXML());

    // теперь вы можете использовать свой объект simpleXML ($xml).

    echo $xml->element_1;

    // перемещение указателя на следующий продукт

    $xml_reader->next('product');

}

// не забываем закрыть файл

$xml_reader->close();

 

Схожие статьи

Web

Преобразование строки JSON в SELECT

Web

Преобразование форматов даты в PHP

Web

Laravel не видит модель

Блочные и строчные элементы в HTML: подробное руководство
Web

Блочные и строчные элементы в HTML: подробное руководство