Wie verwende ich XMLReader in PHP?


79

Ich habe die folgende XML-Datei, die Datei ist ziemlich groß und ich konnte Simplexml nicht dazu bringen, die Datei zu öffnen und zu lesen, also versuche ich XMLReader ohne Erfolg in PHP

<?xml version="1.0" encoding="ISO-8859-1"?>
<products>
    <last_updated>2009-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>

Ich habe leider kein gutes Tutorial für PHP gefunden und würde gerne sehen, wie ich jeden Elementinhalt in einer Datenbank speichern kann.


1
Haben Sie einige der vom Benutzer bereitgestellten Beispiele in der PHP-Dokumentation gelesen? php.net/manual/en/class.xmlreader.php#61929 kann helfen.
mcrumley

Antworten:


224

Es hängt alles davon ab, wie groß die Arbeitseinheit ist, aber ich denke, Sie versuchen, jeden <product/>Knoten nacheinander zu behandeln .

Der einfachste Weg wäre, XMLReader zu verwenden, um zu jedem Knoten zu gelangen, und dann SimpleXML zu verwenden, um auf sie zuzugreifen. Auf diese Weise halten Sie die Speichernutzung gering, da Sie jeweils nur einen Knoten behandeln und dennoch die Benutzerfreundlichkeit von SimpleXML nutzen. Zum Beispiel:

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

$doc = new DOMDocument;

// move to the first <product /> node
while ($z->read() && $z->name !== 'product');

// now that we're at the right depth, hop to the next <product/> until the end of the tree
while ($z->name === 'product')
{
    // either one should work
    //$node = new SimpleXMLElement($z->readOuterXML());
    $node = simplexml_import_dom($doc->importNode($z->expand(), true));

    // now you can use $node without going insane about parsing
    var_dump($node->element_1);

    // go to next <product />
    $z->next('product');
}

Schneller Überblick über Vor- und Nachteile verschiedener Ansätze:

Nur XMLReader

  • Vorteile: schnell, verbraucht wenig Speicher

  • Nachteile: Sehr schwer zu schreiben und zu debuggen, erfordert viel Userland-Code, um irgendetwas Nützliches zu tun. Userland-Code ist langsam und fehleranfällig. Außerdem stehen Ihnen mehr Codezeilen zur Verfügung

XMLReader + SimpleXML

  • Vorteile: verwendet nicht viel Speicher (nur den Speicher, der für die Verarbeitung eines Knotens benötigt wird) und SimpleXML ist, wie der Name schon sagt, sehr einfach zu verwenden.

  • Nachteile: Das Erstellen eines SimpleXMLElement-Objekts für jeden Knoten ist nicht sehr schnell. Sie müssen es wirklich vergleichen, um zu verstehen, ob es ein Problem für Sie ist. Selbst eine bescheidene Maschine könnte jedoch tausend Knoten pro Sekunde verarbeiten.

XMLReader + DOM

  • Vorteile: Verwendet ungefähr so ​​viel Speicher wie SimpleXML, und XMLReader :: expand () ist schneller als das Erstellen eines neuen SimpleXMLElement. Ich wünschte, es wäre möglich zu verwenden, simplexml_import_dom()aber es scheint in diesem Fall nicht zu funktionieren

  • Nachteile: Es ist ärgerlich, mit DOM zu arbeiten. Es ist auf halbem Weg zwischen XMLReader und SimpleXML. Nicht so kompliziert und umständlich wie XMLReader, aber Lichtjahre von der Arbeit mit SimpleXML entfernt.

Mein Rat: Schreiben Sie einen Prototyp mit SimpleXML und prüfen Sie, ob er für Sie funktioniert. Wenn die Leistung von größter Bedeutung ist, versuchen Sie es mit DOM. Halten Sie sich so weit wie möglich von XMLReader entfernt. Denken Sie daran, je mehr Code Sie schreiben, desto höher ist die Wahrscheinlichkeit, dass Sie Fehler oder Leistungsregressionen einführen.


1
Gibt es eine Möglichkeit, dies nur mit XMLReader zu tun, oder gibt es keinen Vorteil?
Shadi Almosri

2
Sie können dies vollständig mit XMLReader tun. Der Vorteil ist, dass es schneller ist und geringfügig weniger Speicher benötigt. Der Nachteil ist, dass das Schreiben erheblich länger dauern würde und das Debuggen sehr viel schwieriger wäre.
Josh Davis

2
Warum haben Sie nicht einfach $ z-> next ('Produkt') verwendet, als Sie zum ersten Produktknoten gewechselt sind?
duftend

Ich erinnere mich nicht an diesen spezifischen Code, sorry. Wenn ich keine Notiz dazu hinzufügen würde, könnte es sein, dass ich die Möglichkeit übersehen habe.
Josh Davis

1
Der größte Teil der XMLReader-basierten Analyse kann in das Iteratormuster ausgedrückt / eingeschlossen werden. Ich habe einige nützliche Iteratoren und Filter dafür zusammengestellt: git.io/xmlreaderiterator ( gist )
hakre

9

Für mit Attributen formatierte 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();

1
Obwohl der Code viel ausführlicher und manueller ist, um durch XML zu gehen, wird dies Ihre Vernunft retten, da DOMDocument und SimpleXML dazu neigen, Sie zu erraten, was zurückgegeben wird.
b01

6

Die akzeptierte Antwort gab mir einen guten Start, brachte aber mehr Klassen und mehr Verarbeitung, als ich gerne hätte; Das ist also meine Interpretation:

$xml_reader = new XMLReader;
$xml_reader->open($feed_url);

// move the pointer to the first product
while ($xml_reader->read() && $xml_reader->name != 'product');

// loop through the products
while ($xml_reader->name == 'product')
{
    // load the current xml element into simplexml and we’re off and running!
    $xml = simplexml_load_string($xml_reader->readOuterXML());

    // now you can use your simpleXML object ($xml).
    echo $xml->element_1;

    // move the pointer to the next product
    $xml_reader->next('product');
}

// don’t forget to close the file
$xml_reader->close();

6

Den größten Teil meines XML-Parsing-Lebens verbringe ich damit, Nuggets nützlicher Informationen aus LKW-Ladungen XML (Amazon MWS) zu extrahieren. In meiner Antwort wird davon ausgegangen, dass Sie nur bestimmte Informationen wünschen und wissen, wo sich diese befinden.

Ich finde, der einfachste Weg, XMLReader zu verwenden, besteht darin, zu wissen, aus welchen Tags die Informationen stammen sollen, und sie zu verwenden. Wenn Sie die Struktur des XML kennen und es viele eindeutige Tags hat, finde ich, dass die Verwendung des ersten Falls einfach ist. Die Fälle 2 und 3 sollen Ihnen nur zeigen, wie dies bei komplexeren Tags möglich ist. Das geht extrem schnell; Ich habe eine Diskussion über die Geschwindigkeit von Was ist der schnellste XML-Parser in PHP?

Das Wichtigste, an das Sie sich erinnern sollten, wenn Sie ein Tag-basiertes Parsing wie dieses durchführen, ist die Verwendung. Dabei wird if ($myXML->nodeType == XMLReader::ELEMENT) {...überprüft, ob es sich nur um das Öffnen von Knoten und nicht um Leerzeichen oder das Schließen von Knoten oder was auch immer handelt.

function parseMyXML ($xml) { //pass in an XML string
    $myXML = new XMLReader();
    $myXML->xml($xml);

    while ($myXML->read()) { //start reading.
        if ($myXML->nodeType == XMLReader::ELEMENT) { //only opening tags.
            $tag = $myXML->name; //make $tag contain the name of the tag
            switch ($tag) {
                case 'Tag1': //this tag contains no child elements, only the content we need. And it's unique.
                    $variable = $myXML->readInnerXML(); //now variable contains the contents of tag1
                    break;

                case 'Tag2': //this tag contains child elements, of which we only want one.
                    while($myXML->read()) { //so we tell it to keep reading
                        if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') { // and when it finds the amount tag...
                            $variable2 = $myXML->readInnerXML(); //...put it in $variable2. 
                            break;
                        }
                    }
                    break;

                case 'Tag3': //tag3 also has children, which are not unique, but we need two of the children this time.
                    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();
}

2
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));
    }
}

2

XMLReader ist auf der PHP-Site gut dokumentiert . Dies ist ein XML-Pull-Parser. Dies bedeutet, dass er zum Durchlaufen von Knoten (oder DOM-Knoten) eines bestimmten XML-Dokuments verwendet wird. Sie können beispielsweise das gesamte von Ihnen angegebene Dokument folgendermaßen durchgehen:

<?php
$reader = new XMLReader();
if (!$reader->open("data.xml"))
{
    die("Failed to open 'data.xml'");
}
while($reader->read())
{
    $node = $reader->expand();
    // process $node...
}
$reader->close();
?>

Es liegt dann an Ihnen, zu entscheiden, wie mit dem von XMLReader :: expand () zurückgegebenen Knoten umgegangen werden soll .


Wie wird es dazu gebracht, zum nächsten Knoten zu wechseln, nachdem die Verarbeitung eines Knotens abgeschlossen ist?
Shadi Almosri

24
Auch wenn XMLReader auf php.net gut dokumentiert ist, würde ich nicht zustimmen, es ist eine der schlechtesten dokumentierten Funktionen, die ich je gesehen habe, und ich habe php.net schon lange verwendet, und es war der erste Ort, an dem ich mich auf den Weg gemacht habe, um dieses Problem zu lösen Fragen Sie hier :)
Shadi Almosri

Ich bin nicht sicher, ob Sie verstehen, wie XMLReader :: read () von einem Knoten zum anderen wechselt. Die XMLReader-Klasse verwendet auch libxml, eine bekannte Bibliothek, die auch für PHP verfügbar ist, wenn Sie sie sich ansehen möchten.
Percutio

12
Die Idee, dass XMLReader gut dokumentiert ist, ist Unsinn. Das Problem ist, dass wenn Sie nicht wissen, wo Sie anfangen sollen, es Ihnen nirgendwo sagt: Eine Wäscheliste mit Klassenmethoden zu geben, ist nutzlos, wenn Sie nicht die erste Idee haben, welche Sie aufrufen sollen.
Xgretsch

0

Das funktioniert besser und schneller für mich


<html>
<head>
<script>
function showRSS(str) {
  if (str.length==0) {
    document.getElementById("rssOutput").innerHTML="";
    return;
  }
  if (window.XMLHttpRequest) {
    // code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp=new XMLHttpRequest();
  } else {  // code for 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="">Select an RSS-feed:</option>
<option value="Google">Google News</option>
<option value="ZDN">ZDNet News</option>
<option value="job">Job</option>
</select>
</form>
<br>
<div id="rssOutput">RSS-feed will be listed here...</div>
</body>
</html> 

** Die Backend-Datei **


<?php
//get the q parameter from URL
$q=$_GET["q"];

//find out which feed was selected
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);

//get elements from "<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;

//output elements from "<channel>"
echo("<p><a href='" . $channel_link
  . "'>" . $channel_title . "</a>");
echo("<br>");
echo($channel_desc . "</p>");

//get and output "<item>" elements
$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++) {
  //Title
  $item_title = $x->item(0)->getElementsByTagName('title')->item(0)->nodeValue;
  //Link
  $item_link = $x->item(0)->getElementsByTagName('link')->item(0)->nodeValue;
  //Description
  $item_desc = $x->item(0)->getElementsByTagName('description')->item(0)->nodeValue;
  //Category
  $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>");
}
?> 

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.