【问题标题】:How to use XMLReader in PHP?如何在 PHP 中使用 XMLReader?
【发布时间】:2010-12-22 13:16:51
【问题描述】:

我有以下 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 教程,我很想看看如何让每个元素内容存储在数据库中。

【问题讨论】:

标签: php xml parsing simplexml xmlreader


【解决方案1】:

XMLReader 在 PHP site 上有详细记录。这是一个 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()返回的节点。

【讨论】:

  • 处理完一个节点后如何让它移动到下一个节点?
  • 另外,关于 XMLReader 在 php.net 上的文档很好,我不同意,这是我见过的最糟糕的文档功能之一,而且我已经使用 php.net 很长时间了,它是第一个在问这里之前我要去的地方解决这个问题:)
  • 我不确定您是否理解 XMLReader::read() 从一个节点到另一个节点的方式。 XMLReader 类还使用了 libxml,这是一个众所周知的库,如果您想查看它,它也可用于 PHP。
  • XMLReader 有据可查的想法是无稽之谈。问题是,如果你不知道从哪里开始,它不会告诉你任何地方:如果你不知道要调用哪些类方法,那么列出类方法的清单是没有用的。
【解决方案2】:

这完全取决于工作单元有多大,但我猜你是在尝试连续处理每个 &lt;product/&gt; 节点。

为此,最简单的方法是使用 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。请记住,您编写的代码越多,引入错误或引入性能回归的可能性就越大。

【讨论】:

  • 有没有办法纯粹用 XMLReader 做到这一点,还是没有优势?
  • 您可以完全使用 XMLReader 来完成。优点是它会更快并且使用更少的内存。缺点是编写需要相当长的时间并且调试起来非常困难。
  • 移动到第一个产品节点时为什么不直接使用 $z->next('product') ?
  • 我不记得那个具体的代码了,抱歉。如果我没有添加任何注释,那可能只是我忽略了这种可能性。
  • 大多数基于 XMLReader 的解析都可以表达/包装到迭代器模式中。我为此编译了一些有用的迭代器和过滤器:git.io/xmlreaderiterator (gist)
【解决方案3】:

对于带有属性格式的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();

【讨论】:

  • 尽管通过 XML 的代码更加冗长和手动,但这将节省您的理智,因为 DOMDocument 和 SimpleXML 往往会让您猜测将返回什么。
【解决方案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】:

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

    $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(亚马逊 MWS)中提取有用信息的块。因此,我的回答假设您只需要特定信息并且您知道它的位置。

      我发现使用 XMLReader 最简单的方法是知道我想从哪些标签中获取信息并使用它们。如果您知道 XML 的结构并且它有很多独特的标签,我发现使用第一种情况很容易。案例 2 和案例 3 只是为了向您展示如何处理更复杂的标签。这是非常快的;我在What is the fastest XML parser in PHP? 上讨论过速度

      像这样进行基于标签的解析时要记住的最重要的事情是使用if ($myXML-&gt;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();
      }
      

      【讨论】:

        【解决方案7】:

        这对我来说效果更好更快

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

        【讨论】:

          猜你喜欢
          • 2012-08-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多