如何在PHP中使用XMLReader?


79

我有以下XML文件,该文件相当大,并且我无法获取simplexml来打开和读取该文件,因此我在尝试XMLReader,但在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>

不幸的是,我还没有找到关于PHP的很好的教程,很想看看我如何才能将每个元素的内容存储在数据库中。


1
您是否已阅读PHP文档中的一些用户贡献的示例?php.net/manual/en/class.xmlreader.php#61929可能会有所帮助。
mcrumley

Answers:


225

这完全取决于工作单元的大小,但是我想您正在尝试<product/>连续对待每个节点。

为此,最简单的方法是使用XMLReader到达每个节点,然后使用SimpleXML访问它们。这样,您可以将内存使用率保持在较低水平,因为您一次要处理一个节点,并且仍然利用SimpleXML的易用性。例如:

$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');
}

快速概述不同方法的优缺点:

仅XMLReader

  • 优点:速度快,占用内存少

  • 缺点:很难编写和调试,需要大量的用户区代码才能做有用的事情。用户域代码很慢,容易出错。另外,它为您保留了更多代码行

XMLReader + SimpleXML

  • 优点:不占用太多内存(仅处理一个节点所需的内存),顾名思义,SimpleXML非常易于使用。

  • 缺点:为每个节点创建SimpleXMLElement对象不是很快。您真的必须对其进行基准测试,以了解它是否对您有问题。不过,即使是一台普通的机器,也能够每秒处理一千个节点。

XMLReader + DOM

  • 优点:使用的内存与SimpleXML差不多,并且XMLReader :: expand()比创建新的SimpleXMLElement更快。我希望可以使用,simplexml_import_dom()但在这种情况下似乎不起作用

  • 缺点:DOM很烦人。它位于XMLReader和SimpleXML之间。不像XMLReader那样复杂和尴尬,但是距离使用SimpleXML尚有数年之遥。

我的建议:使用SimpleXML编写原型,看看它是否对您有用。如果性能至关重要,请尝试使用DOM。尽可能远离XMLReader。请记住,编写的代码越多,引入错误或引入性能退化的可能性就越大。


1
有没有办法完全使用XMLReader做到这一点,还是没有优势?
Shadi Almosri

2
您可以完全使用XMLReader做到这一点。这样做的好处是速度更快并且使用的内存更少。缺点是编写时间会长得多,调试起来会非常困难。
乔什·戴维斯

2
为什么移到第一个产品节点时不只使用$ z-> next('product')?
redolent 2012年

我不记得那个特定的代码,对不起。如果我没有对此添加任何说明,则可能只是我忽略了这种可能性。
乔什·戴维斯

1
大多数基于XMLReader的解析都可以表示/包装为迭代器模式。为此,我已经编译了一些有用的迭代器和过滤器:git.io/xmlreaderiteratorgist
hakre 2013年

10

对于具有属性格式的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代码:

$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
即使代码是遍历XML的冗长和手动方式,这也可以节省您的精力,因为DOMDocument和SimpleXML倾向于让您猜测返回的内容。
B01

6

被接受的答案给了我一个良好的开端,但是带来了更多的类和更多的处理;所以这是我的解释:

$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

我的大部分XML解析时间都花在从大量的XML(Amazon MWS)中提取有用信息的块中。因此,我的答案假设您只需要特定的信息,并且知道它的位置。

我发现使用XMLReader的最简单方法是知道要从哪个标签中获取信息并使用它们。如果您知道XML的结构并且它具有许多独特的标记,那么我会发现使用第一种情况很容易。情况2和3只是向您展示如何对更复杂的标签进行处理。这非常快。我讨论了PHP中最快的XML解析器是什么?

在进行像这样的基于标记的解析时,要记住的最重要的事情是-使用此方法来if ($myXML->nodeType == XMLReader::ELEMENT) {...检查以确保我们只处理打开节点,而不处理空格或关闭节点等。

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在 PHP网站上有详细记录。这是一个XML Pull Parser,这意味着它用于迭代给定XML文档的节点(或DOM节点)。例如,您可以像这样浏览整个文档:

<?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();
?>

然后由您决定如何处理XMLReader :: expand()返回的节点。


处理完一个节点后,如何将其移动到下一个节点?
Shadi Almosri 09年

24
同样,关于XMLReader在php.net上有很好的文档记录,我也不同意,它是我见过的最糟糕的文档化函数之一,并且我使用php.net已有很长时间了,这是我之前想解决的第一个地方在这里问:)
Shadi Almosri 09年

我不确定您了解XMLReader :: read()从一个节点到另一个节点的方式。XMLReader类还使用libxml,这是一个众所周知的库,如果您想看一下的话,PHP也可以使用它。
Percutio

12
充分记录XMLReader的想法是胡说八道。问题是,如果您不知道从哪里开始,它不会告诉您任何地方:如果您根本不知道要调用哪个方法,那么给出类方法的清单是没有用的。
xgretsch 2011年

0

这对我来说更好更快


<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> 

**后端文件**


<?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>");
}
?> 

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.