0

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

<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 功能相比,php.net 上的 XMLReader 文档严重缺乏,而且通常似乎很难找到清晰简洁的详细指南。

4

2 回答 2

4

好吧,如果您仍然可以构建该数组,那么您的 XML 文件可能不会那么大:)。例如,尝试使用 simplexml 加载整个文件,您可能会惊讶它不会消耗那么多内存。

无论如何,如果您仍然想使用 XMLReader,我经常建议使用我的XMLReader Iterator 库,该库能够遍历 anXMLReader以访问元素、子项并执行诸如将片段转换为SimpleXMLElements 之类的操作。

以下是与您上面的示例几乎相同的示例:

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
        )

)

从技术上讲,您不需要使用该库,它仅an上运行,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输出所示, englishName 键设置为 simplexml 元素。您可能希望像我在示例(这两部分)中所做的那样将它们转换为字符串,以便在其中使用字符串而不是 SimpleXMLElements,这可能是您的问题。如果没有,请检查您的 libxml 版本:$items(string)

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

并将其报告回来(即库XMLReader所基于的)。同时调试您的 SimpleXMLElement ( var_dump($sxml->asXML());),以便您可以检查预期的 XML 是否已加载。

顺便说一句,我建议的图书馆。如果您想快速尝试,还附带一个包含文件。

上次我建议图书馆在:


编辑:一个额外的混合版本,没有显示使用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,
    );
}
于 2013-06-23T17:37:13.020 回答
-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;
}
于 2013-06-23T17:40:12.737 回答