取消设置子节点将使方法消失


Unset child node makes method disappear

我用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();