我用SimpleXML解析了一个XML,想截断它。这是方法:
public function truncate(SimpleXMLElement $xml)
{
foreach ($xml->children() as $key => $value)
{
if ( !empty($key) && isset($xml->$key))
{
echo "unset $key";
unset($xml->$key);
}
}
echo $xml->asXML();
exit;
}
第一次unset后抛出:
警告:pat'Persistence'File'Text'XML::truncate(): Node不再存在于pat/Persistence/File/Text/XML.php的第36行
我不想取消这个方法
simplexml中的错误信息"Node no longer exists"表示您正在尝试删除一个不再存在的节点。
正如您已经注意到的,这不会发生在第一个迭代的unset中,而是发生在接下来的迭代中。如果您现在想象 SimpleXMLElement::children()
返回一个以子元素的从零开始的索引为键,以子元素节点为值的数组,请考虑以下内容以更好地理解:
元素有两个子元素。因此,children()
函数返回一个包含两个元素的数组。
$children = [ 0 => <child1>, 1 => <child2> ]
^ unset($xml->{0});
`-----------------^
$xml->children() = [ 0 => <child1>, 1 => <child2> ]
现在删除第一个子元素,索引0。
第二个子元素还在:
$xml->children() = [ 0 => <child2> ]
但是因为你是根据子节点的编号来访问它们的,并且第一个子节点被删除了,所以第二个子节点得到了一个新的索引:从之前的1变成了0。但是,您取消了在索引1处的子节点设置:
$children = [ 0 => <child1>, 1 => <child2> ]
^ unset($xml->{1});
`-----------------^
$xml->children() = [ 0 => <child2> ]
已经不存在了:
节点不存在
看到错误信息了吗?怎么解呢?SimpleXML中常见的一种方法是使用SimpleXML自引用,正如hakre在问题中概述的那样:
unset($value[0]);
如您所见,它不需要使用任何键。另一个选择是反转子数组并从末尾取消设置,而不是从开始。这样你就不会把索引拖到你的脚下:
$children = array_reverse($xml->children());
foreach ($children as $key => $value) {
...
unset($xml->$key);
}
}
但是self-reference可能更容易使用,因为它也适用于其他情况,当你想要在simplexml中取消某些设置时,所以值得你阅读那里的详细解释。
完整示例一览:
public function truncate()
{
$xml = $this->_xml;
$children = $xml->children();
foreach ($children as $index => $child)
{
if ($index) {
unset($child[0]);
}
}
$xml->asXML('php://output');
exit;
}
我还会说exit
不属于该方法,但我留下了它,以便您可以更好地定位。相反,该方法应该只输出截断的XML,而不关心脚本的结尾。将其移出并放在调用方法之后,因为这是两种不同的任务:一种是XML处理和输出,另一种是程序流。
如果您想删除空的子元素,那么您正在读取子元素,但是从父元素中删除试试这样做:
foreach ($xml->children() as $child)
{
if (empty($child)) {
unset($child[0]);
}
}
echo $xml->asXML();