2013-06-23 138 views
0

我正在尝试编写一个XMLReader/SimpleXML混合函数来读取一个非常大的(700MB)XML文件。该XML的格式如下:用XMLReader读取子节点

<Items> 
    <Item> 
     <ItemKey>ABCDEF123</ItemKey> 
     <Name> 
      <English>An Item Name</English> 
      <German>An Item Name In German</German> 
      <French>An Item Name In French</French> 
     </Name> 
     <Description> 
      <English>An Item Description</English> 
      <German>An Item Description In German</German> 
      <French>An Item Description In French</French> 
     </Description> 
    </Item> 
    <Item> 
     <ItemKey>GHIJKL456</ItemKey> 
     <Name> 
      <English>Another Item Name</English> 
      <German>Another Item Name In German</German> 
      <French>Another Item Name In French</French> 
     </Name> 
     <Description> 
      <English>Another Item Description</English> 
      <German>Another Item Description In German</German> 
      <French>Another Item Description In French</French> 
     </Description> 
    </Item> 
</Items> 

到目前为止,我写的代码来做到这一点:

$xml = new XMLReader(); 
if(!$xml->open('testitems.xml')){ 
    die('Failed to open file!'); 
} else { 
    echo 'File opened'; 
} 

$items = array(); 

while ($xml->read()){ 
    if($xml->nodeType == XMLReader::ELEMENT){ 
     if ($xml->name == 'Item'){ 
      $item = array(); 
     } 

     if ($xml->name == 'ItemKey'){ 
      $xml->read(); 
      $item['itemKey'] = $xml->value; 
     } 
     if ($xml->name == 'Name'){ 
      $sxml = new SimpleXMLElement($xml->readOuterXml()); 
      $englishName = $sxml->English; 
      $item['englishName'] = $englishName; 
     } 
    } 
    if($xml->nodeType == XMLReader::END_ELEMENT){ 
     if ($xml->name == 'Item'){ 
      $items[] = $item; 
     } 
    } 
} 
var_dump($items); 
$xml->close(); 

然而,当的ItemKey节点值被插入到阵列中,英文名称为不,我似乎无法正确访问此节点。我只是用XMLReader来处理所有事情,但是由于从目前为止我的谷歌搜索中重复出现了英文节点(一个用于名称,另一个用于描述),SimpleXML似乎是前进的方向,但目前还没有喜悦。

有什么建议吗?任何好的导游? php.net上的XMLReader文档与许多其他PHP功能相比是非常缺乏的,通常看起来很难找到清晰简洁的详细指南。

回答

-1

没关系,想通了。对于任何被卡住的人:

$xml = new XMLReader(); 
if(!$xml->open('Items.xml')){ 
    die('Failed to open file!'); 
} else { 
    echo 'File opened'; 
} 

$items = array(); 

while ($xml->read() && $xml->name !== "Item"); 
while ($xml->name === "Item") { 
    $item = array(); 
    $node = new SimpleXMLElement($xml->readOuterXML()); 
    $item['itemkey'] = $node->ItemKey; 
    $item['englishName'] = $node->Name->English; 
    $item['englishDesc'] = $node->Description->English; 
    $items[] = $item; 
} 
+0

这基本上就是我所拥有的,但它仍然不清楚为什么你的原始代码不起作用。正如我已经回答的那样,我确实运行了它,并且它在我的最后工作。在这里你几乎做同样的事情,现在也运行。 – hakre

+0

是的,我最初写了类似于这个工作版本的东西,它不会通过SimpleXML给我节点值,然后突然它。奇怪,但在编码时似乎很常见,哈哈。可能是一个小的语法错误。 顺便说一句,我看到你发布你的解决方案之前,并注意到有人评论说,XMLReader/SimpleXML混合(我采取的路线)比纯XMLReader方法更快,你试过两种方法吗?哪个更快?我设法在53秒内解析大约90k条记录,这与我的SimpleXML only方法(2小时!)相比有了很大的改进 – MikeK

+0

好吧,如果仔细观察,混合方法就是我的答案的一部分:'$ item-> asSimpleXML();'这将返回 - 猜测:) - 是的。它只是在其中实施,所以我不需要再在意了。由于libxml版本的原因,它也支持不支持'readOuterXML()'的系统。由于迭代器只是一个附加组件,您可以说这基本上是一样的。在你的情况下,你可以做一个额外的技巧,我会破解它,并扩大我的答案只是为了可读性。 – hakre

3

那么如果你仍然可以建立这个数组,你的XML文件可能并不那么大:)。尝试使用simplexml加载整个文件,例如,您可能会惊讶于它不会占用太多的内存。

无论如何,如果你仍然想使用XMLReader的,我经常建议我XMLReader Iterator library即能遍历一个XMLReader访问元素,孩子和做的东西一样转动碎片进入SimpleXMLElement秒。

以下是这几乎等同于你上面的例子的示例:

require('xmlreader-iterators.php'); // https://github.com/hakre/XMLReaderIterator/tree/master/build/include 

$xmlFile = "xmlreader-17262798.xml"; 

$reader = new XMLReader(); 
$reader->open($xmlFile); 

/* @var $itemIterator XMLReaderNode[] */ 
$itemIterator = new XMLElementIterator($reader, 'Item'); 

$items = array(); 

foreach ($itemIterator as $item) { 
    $xml  = $item->asSimpleXML(); 
    $items[] = array(
     'itemKey'  => (string)$xml->ItemKey, 
     'englishName' => (string)$xml->Name->English, 
    ); 
} 

当您在演示数据运行它,所得$items阵列是:

Array 
(
    [0] => Array 
     (
      [itemKey] => ABCDEF123 
      [englishName] => An Item Name 
     ) 

    [1] => Array 
     (
      [itemKey] => GHIJKL456 
      [englishName] => Another Item Name 
     ) 

) 

技术上你不不需要使用该库,它仅在和XMLReader上运行,因此它不会改变XMLReader的工作方式。这是一个附加组件。

为什么不能在您的特定情况下工作,就很难说了,你的代码并在我的电脑上运行完美:

Array 
(
    [0] => Array 
     (
      [itemKey] => ABCDEF123 
      [englishName] => SimpleXMLElement Object 
       (
        [0] => An Item Name 
       ) 

     ) 

    [1] => Array 
     (
      [itemKey] => GHIJKL456 
      [englishName] => SimpleXMLElement Object 
       (
        [0] => Another Item Name 
       ) 

     ) 

) 

由于这种print_r$items(代码)输出显示,该中文名键被设置为simplexml元素。你可能可能想要将它们转换为字符串,就像我在我的示例中所做的那样(这两个(string)部分)在那里有字符串而不是SimpleXMLElements,这可能是您的问题。如果没有,请检查您的libxml版本:

var_dump(LIBXML_DOTTED_VERSION); # string(5) "2.7.8" 

它报到(即库XMLReader是基于)。还要调试您的SimpleXMLElement(var_dump($sxml->asXML());),以便您可以检查预期的XML是否已加载。

我建议btw的图书馆。如果您想快速尝试,还会附带a single include file

我上次建议库是:


编辑:另外一个,混合版本的w/o表示使用next(),其是库随着您对相同名字的兄弟姐妹进行迭代:<Item>

$xmlFile = "xmlreader-17262798.xml"; 

$reader = new XMLReader(); 
$reader->open($xmlFile); 

$reader->read() && $reader->read(); // init and position onto first element 

$items = array(); 
while ($reader->next('Item')) { 
    $node = new SimpleXMLElement($reader->readOuterXML()); 

    $items[] = array(
     'itemkey'  => $node->ItemKey, 
     'englishName' => $node->Name->English, 
     'englishDesc' => $node->Description->English, 
    ); 
}