【问题标题】:Why is this xmlreader code not working?为什么这个 xmlreader 代码不起作用?
【发布时间】:2013-01-30 19:44:12
【问题描述】:

我有一个如下所示的文件:

    <ExternalPage about="http://animation.about.com/">
       <d:Title>About.com: Animation Guide</d:Title>
       <d:Description>Keep up with developments in online animation for all skill levels.     Download tools, and seek inspiration from online work.</d:Description>
       <topic>Top/Arts/Animation</topic>
    </ExternalPage>
    <ExternalPage about="http://www.toonhound.com/">
       <d:Title>Toonhound</d:Title>
       <d:Description>British cartoon, animation and comic strip creations - links, reviews  and news from the UK.</d:Description>
       <topic>Top/Arts/Animation</topic>
    </ExternalPage>

等等

我正在尝试获取“关于”网址,以及嵌套的标题和描述。我试过下面的代码,但我得到的只是一堆破折号......

$reader = new XMLReader();

if (!$reader->open("dbpedia/links/xml.xml")) {
die("Failed to open 'xml.xml'");
}
$num=0;
while($reader->read() && $num<200) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'ExternalPage') {
$url = $reader->getAttribute('about');

while ($xml->nodeType !== XMLReader::END_ELEMENT ){
$reader->read();

 if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'd:Title') {
 $title=$xmlReader->value;
 }
elseif ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'd:Description') {
$desc=$xmlReader->value;
}
}

}
$num++;echo $url."-".$title."-".$desc."<br />";
}
$reader->close();

我是 xmlreader 的新手,所以如果有人能找出我做错了什么,我将不胜感激。

注意:我使用 xmlreader 是因为文件很大(数百万行)。

编辑:文件的开头如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns:r="http://www.w3.org/TR/RDF/" xmlns:d="http://purl.org/dc/elements/1.0/"       xmlns="http://dmoz.org/rdf/">
  <!-- Generated at 2013-02-10 00:03:45 EST from DMOZ 2.0 -->
  <Topic r:id="">
<catid>1</catid>
  </Topic>
<Topic r:id="Top/Arts">
    <catid>381773</catid>
  </Topic>
  <Topic r:id="Top/Arts/Animation">
  <catid>423945</catid>
<link1 r:resource="http://www.awn.com/"></link1>
<link r:resource="http://animation.about.com/"></link>
<link r:resource="http://www.toonhound.com/"></link>
<link r:resource="http://enculturation.gmu.edu/2_1/pisters.html"></link>
<link r:resource="http://www.digitalmediafx.com/Features/animationhistory.html"></link>
<link r:resource="http://www.spark-online.com/august00/media/romano.html"></link>
<link r:resource="http://www.animated-divots.net/"></link>
</Topic>
<ExternalPage about="http://www.awn.com/">
<d:Title>Animation World Network</d:Title>
<d:Description>Provides information resources to the international animation community. Features include searchable database archives, monthly magazine, web animation guide, the Animation Village, discussion forums and other useful resources.</d:Description>
<priority>1</priority>
<topic>Top/Arts/Animation</topic>
</ExternalPage>

【问题讨论】:

  • 您能否将d: 的命名空间声明和根标记添加到示例XML?
  • @DoSparKot 我将文件的整个第一部分添加到问题中。

标签: php xml xml-parsing xmlreader


【解决方案1】:

编写纯 XMLReader 代码需要时间和适当的调试。同时尝试这种混合方法:

$xmlR = new XMLReader;
$xmlR->open('dbpedia/links/xml.xml');

//Skip until <ExternalPage> node
while ($xmlR->read() && $xmlR->name !== 'ExternalPage');

$loadedNS_f = false;
while ($xmlR->name === 'ExternalPage')
{
    //Read the entire parent tag with children
    $sxmlNode = new SimpleXMLElement($xmlR->readOuterXML());

    //collect all namespaces in node recursively once; assuming all nodes are similar
    if (!$loadedNS_f) {
        $tagNS = $sxmlNode->getNamespaces(true);
        $loadedNS_f = true; 
    }
    $URL = (string) $sxmlNode['about'];
    $dNS = $sxmlNode->children($tagNS['d']);
    $Title = (string) $dNS->Title;
    $Desc = (string) $dNS->Description;
    $Topic = (string)$sxmlNode->topic;

    var_dump($URL, $Title, $Desc, $Topic);

    // Jump to next <ExternalPage> tag
    $xmlR->next('ExternalPage');
}

$xmlR->close();

【讨论】:

  • 谢谢,效果很好!我担心它可能会很慢,因为它不是纯 xmlreader,但它在大约 20 秒内处理了超过 120k 条记录,所以这非常好!
  • 没有真正需要通过 XML 字符串缓冲区进入 SimpleXML。对于元素节点值,您可以使用通过XMLReader::expand()-&gt;nodeValue 提供的DOMNode-&gt;nodeValue,并且当您自己封装一点时,孩子也很容易解析:stackoverflow.com/a/14904227/367456 - 看看这是否会产生一些有趣的毕竟区别。
【解决方案2】:

它不适合你的原因是因为你只读取了d:Title 元素的开始标签,而那个标签没有任何价值:

if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'd:Title') {
    $title=$xmlReader->value;
}

您可能想要获取该 DOM 元素的 nodeValue,但这不是 $xmlReader-&gt;value 将返回的内容。知道这一点,有多种方法可以解决:

  1. 展开节点(XMLReader::expand())并获得nodeValue(快速示例):

    $title = $reader->expand()->nodeValue;
    
  2. 自行处理所有XMLReader::TEXT (3) 和/或XMLReader::CDATA (4) 子节点(通过查看XMLReader::$depth 确定节点是否为子节点)。

在任何情况下,为了简化您的代码,您都可以考虑直接提供您需要的东西,例如通过自己创建一组函数或扩展 XMLReader 类:

class MyXMLReader extends XMLReader
{
    public function readToNextElement()
    {
        while (
            $result = $this->read()
            and $this->nodeType !== self::ELEMENT
        ) ;
        return $result;
    }

    public function readToNext($localname)
    {
        while (
            $result = $this->readToNextElement()
            and $this->localName !== $localname
        ) ;
        return $result;
    }

    public function readToNextChildElement($depth)
    {
        // if the current element is the parent and
        // empty there are no children to go into
        if ($this->depth == $depth && $this->isEmptyElement) {
            return false;
        }

        while ($result = $this->read()) {
            if ($this->depth <= $depth) return false;
            if ($this->nodeType === self::ELEMENT) break;
        }

        return $result;
    }

    public function getNodeValue($default = NULL)
    {
        $node = $this->expand();
        return $node ? $node->nodeValue : $default;
    }
}

然后您可以使用这个扩展类来进行处理:

$reader = new MyXMLReader();
$reader->open($uri);

$num = 0;
while ($reader->readToNext('ExternalPage') and $num < 200) {
    $url = $reader->getAttribute('about');

    $depth = $reader->depth;
    $title = $desc = '';

    while ($reader->readToNextChildElement($depth)) {
        switch ($reader->localName) {
            case 'Title':
                $title = $reader->getNodeValue();
                break;
            case 'Description':
                $desc = trim($reader->getNodeValue());
                break;
        }
    }

    $num++;
    echo "#", $num, ": ", $url, " - ", $title, " - ", $desc, "<br />\n";
}

如您所见,这极大地提高了您的代码的可读性。如果你没看错,你也不需要每次都在意。

【讨论】:

  • 纯XMLReader,我还是不明白为什么它慢了将近五倍?我预计性能至少提高 20%。我们可以进一步优化吗?
  • 您可以尝试只在 your 代码示例中执行expand 操作。可能是循环没有优化(尤其是在有孩子的情况下)。此外,您可能会认为XMLReader 更快,但您的文档并不是很大,因此它可能没有任何用处。您可能想查看 XML 解析器:php.net/book.xml.php - 它可以处理 XML 块。
  • 我之前启用了 X-Debug 功能;我禁用了它们。我用 6500 xml 标签文件进行了测试。无论如何...现在它慢了近两倍。
  • 我没有看到你的代码,所以很难给你任何具体的反馈。您正在比较哪两个代码?对于 XML 文档,6500 个标签对我来说根本不算大。通过选择XML_Reader,您确实可能过度优化了自己。首先一个简单的 SimpleXML 甚至可能更快,尤其是运行一些 xpath 查询。
  • OPs XML 有数百万条记录。比较代码:我的 XMLReader + SimpleXML combo 和你的 eXtended XMLReader
【解决方案3】:

这是获取该属性的另一种方法:

$string = file_get_contents($filename);
$xml = new SimpleXMLElement($string);
$result = $xml->xpath('/RDF/ExternalPage[*]/@about');
var_dump($result);

【讨论】:

  • 我的理解是 simplexml 根本不能很好地处理大文件,这就是我使用 xmlreader 的原因。
  • 试试看。我想你会感到惊讶。 @DoSparKot 有一个很好的混合解决方案。
猜你喜欢
  • 2018-11-05
  • 2012-02-22
  • 2023-03-03
  • 2017-10-02
  • 2016-07-10
  • 2010-12-14
  • 2017-04-09
  • 2014-05-23
  • 1970-01-01
相关资源
最近更新 更多