我有一个非常大的 xml 文件,格式如下(这是其中两个部分的一个非常小的片段)。
<?xml version="1.0" standalone="yes"?>
<LaunchBox>
<Game>
<Name>Violet</Name>
<ReleaseYear>1985</ReleaseYear>
<MaxPlayers>1</MaxPlayers>
<Platform>ZiNc</Platform>
</Game>
<Game>
<Name>Wishbringer</Name>
<ReleaseYear>1985</ReleaseYear>
<MaxPlayers>1</MaxPlayers>
<Platform>ZiNc</Platform>
</Game>
<Platform>
<Name>3DO Interactive Multiplayer</Name>
<Emulated>true</Emulated>
<ReleaseDate>1993-10-04T00:00:00-07:00</ReleaseDate>
<Developer>The 3DO Company</Developer>
</Platform>
<Platform>
<Name>Commodore Amiga</Name>
<Emulated>true</Emulated>
<ReleaseDate>1985-07-23T00:00:00-07:00</ReleaseDate>
<Developer>Commodore International</Developer>
</Platform>
</LaunchBox>
我想快速找到所有父元素的实例(即上例中的
Game
和Platform
)以计算它们并提取内容。
更复杂的是,
Platform
里面还有一个Game
“孩子”(我不想算)。我只想要父母(即我不想要Game -> Platform
但我只想要Platform
.
结合本网站和谷歌,我得出了以下功能代码:
$attributeCount = 0;
$xml = new XMLReader();
$xml->open($xmlFile);
$elements = new \XMLElementIterator($xml, $sectionNameWereGetting);
// $sectionNameWereGetting is a variable that changes to Game and Platform etc
foreach( $elements as $key => $indElement ){
if ($xml->nodeType == XMLReader::ELEMENT && $xml->name == $sectionNameWereGetting) {
$parseElement = new SimpleXMLElement($xml->readOuterXML());
// NOW I CAN COUNT IF THE ELEMENT HAS CHILDREN
$thisCount = $parseElement->count();
unset($parseElement);
if ($thisCount == 0){
// IF THERE'S NO CHILDREN THEN SKIP THIS ELEMENT
continue;
}
// IF THERE IS CHILDREN THEN INCREMENT THE COUNT
// - IN ANOTHER FUNCTION I GRAB THE CONTENTS HERE
// - AND PUT THEM IN THE DATABASE
$attributeCount++;
}
}
unset($elements);
$xml->close();
unset($xml);
return $attributeCount;
我正在使用 Hakre 的优秀脚本 https://github.com/hakre/XMLReaderIterator/blob/master/src/XMLElementIterator.php
这确实有效。但我认为分配一个新的 SimpleXMLElement 会减慢操作速度。
我只需要 SimpleXMLElement 来检查元素是否有子元素(我用它来确定元素是否在另一个父元素中 - 即如果它是父元素,它“将”有子元素,所以我想计算它但是,如果它在另一个父母里面那么它不会有孩子,我想忽略它)。
但也许有比数孩子更好的解决方案?即
$xml->isParent()
函数之类的?
当前函数在完全统计xml的所有部分之前超时(大约有8个不同的部分,其中一些有几十万条记录)。
我怎样才能让这个过程更有效率,因为我也在使用类似的代码来获取主要部分的内容并将它们放入数据库中,这样它就会尽可能高效地支付红利。
另外值得注意的是,我不是特别擅长编程,所以请随时指出我可能犯的其他错误,以便我改进。
听起来使用 xpath 而不是遍历 XML 可能适用于您的用例。使用 xpath,您可以选择所需的特定节点:
$xml = simplexml_load_string($xmlStr);
$games = $xml->xpath('/LaunchBox/Game');
echo count($games).' games'.PHP_EOL;
foreach ($games as $game) {
print_r($game);
}
您无需序列化 XML 即可将其加载到 DOM 或 SimpleXML 中。你可以扩展成 DOM 文档:
$reader = new XMLReader();
$reader->open(getXMLDataURL());
$document = new DOMDocument();
// navigate using read()/next()
while ($found) {
// expand into DOM
$node = $reader->expand($document);
// import DOM into SimpleXML
$simpleXMLObject = simplexml_import_dom($node);
// navigate using read()/next()
}
但是,只需对
XMLReader:read()
和 XMLReader:next()
进行正确的调用,就可以计算文档元素的元素子元素。 read()
将导航到包含后代的以下节点,而 next()
将导航到以下同级节点 - 忽略后代。
$reader = new XMLReader();
$reader->open(getXMLDataURL());
$document = new DOMDocument();
$xpath = new DOMXpath($document);
$found = false;
// look for the document element
do {
$found = $found ? $reader->next() : $reader->read();
} while (
$found &&
$reader->localName !== 'LaunchBox'
);
// go to first child of the document element
if ($found) {
$found = $reader->read();
}
$counts = [];
while ($found) {
if ($reader->nodeType === XMLReader::ELEMENT) {
if (isset($counts[$reader->localName])) {
$counts[$reader->localName]++;
} else {
$counts[$reader->localName] = 1;
}
}
// go to next sibling node
$found = $reader->next();
}
var_dump($counts);
function getXMLDataURL() {
$xml = <<<'XML'
<?xml version="1.0" standalone="yes"?>
<LaunchBox>
<Game>
<Name>Violet</Name>
<ReleaseYear>1985</ReleaseYear>
<MaxPlayers>1</MaxPlayers>
<Platform>ZiNc</Platform>
</Game>
<Game>
<Name>Wishbringer</Name>
<ReleaseYear>1985</ReleaseYear>
<MaxPlayers>1</MaxPlayers>
<Platform>ZiNc</Platform>
</Game>
<Platform>
<Name>3DO Interactive Multiplayer</Name>
<Emulated>true</Emulated>
<ReleaseDate>1993-10-04T00:00:00-07:00</ReleaseDate>
<Developer>The 3DO Company</Developer>
</Platform>
<Platform>
<Name>Commodore Amiga</Name>
<Emulated>true</Emulated>
<ReleaseDate>1985-07-23T00:00:00-07:00</ReleaseDate>
<Developer>Commodore International</Developer>
</Platform>
</LaunchBox>
XML;
return 'data:application/xml;base64,'.base64_encode($xml);
}
输出:
array(2) {
["Game"]=>
int(2)
["Platform"]=>
int(2)
}