使用缺少的节点解析XML

时间:2013-02-06 15:07:05

标签: php xml simplexml

我有一个包含订单的XML文件。

<?xml version="1.0" encoding="UTF-8"?>
<orders>
<order order-no="1234567">
    <order-date>2012-09-15T12:28:45.000Z</order-date>
    <invoice-no>123</invoice-no>
    <customer>
        <customer-no>1234</customer-no>
        <customer-name>test</customer-name>
    </customer>
    <payments>
        <payment>
            <custom-method>
                <method-name>example</method-name>
            </custom-method>
            <amount>12334</amount>
        </payment>
    </payments>
</order>
<order>
   .......
</order>
</orders>

一个文件中有许多<order>个节点。一切都是正确的但是我突然得到一个没有任何支付信息的订单

<orders>
<order order-no="1234567">
    <order-date>2012-09-15T12:28:45.000Z</order-date>
    <invoice-no>123</invoice-no>
    <customer>
        <customer-no>1234</customer-no>
        <customer-name>test</customer-name>
        <billing-address/>
    </customer>
    <payments/>
</order>

注意空<payments/>节点。 我的PHP脚本如下所示:

$file = file_get_contents('orders.xml');
$orders = new SimpleXMLElement($file);
foreach ($orders as $order) {
    $var = $order->payments->payment->{'custom-method'}->{'method-name'};
}

使用第一个XML示例一切都很好但是当我偶然发现第二个XML示例中的订单时,我的脚本会抛出异常Trying to get property of non-object并显然停止解析文件的其余部分。
我喜欢我的脚本停止并继续解析文件的其余部分。我不想要你关闭&#34;闭嘴&#34; @运营商,但我没有看到任何其他方式。

2 个答案:

答案 0 :(得分:1)

您需要添加逻辑以确保<payments>具有付款数据(即payment个节点)。

foreach ($orders as $order) {
    if ($order->payments->count()) {
        // has payment data... run your payment code
    }
}

更新

即使上面的docs claim适用于PHP&gt; 5.3,它在OP的情况下返回1(不确定原因)。

最终需要以下内容:

count($order->payments->children())

答案 1 :(得分:0)

SimpleXML带有一些 magic 。让我们看看您如何访问(现有的或不存在的)<payment>元素:

$order->payments->payment

无论SimpleXMLElement元素中<payment>个元素的数量是零还是更大,这将(始终!)产生<payments>

但是,如果计数实际为零,则访问第一个<payment>元素将返回NULL

$order->payments->payment[0]

这是访问第一个(SimpleXML中从零开始的索引)付款元素。因为它不存在,导致NULL。所以你可以检查一下:

foreach ($orders as $order) 
{
    if (!$order->payments->payment[0]) 
        continue;

    $var = $order->payments->payment->{'custom-method'}->{'method-name'};
}

另一个答案建议使用SimpleXMLElement::count()方法。它计算一个元素的子元素并且它也可以工作,但另一个答案表明使用它的位置错误,正确的位置是<payment>元素,而不是<payments>元素:

$order->payments->payment->count()

这将返回您要检查的0(零)个元素。所以这同样有效:

foreach ($orders as $order) 
{
    if (!$order->payments->payment->count()) 
        continue;

    $var = $order->payments->payment->{'custom-method'}->{'method-name'};
}

然而,这次游览并不能完全显示出一种可能最适合SimpleXMLElement的方式。它基于以下事实:空SimpleXMLElement等于false:

foreach ($orders as $order) 
{
    if (!$order->payments->payment) 
        continue;

    $var = $order->payments->payment->{'custom-method'}->{'method-name'};
}

正如所有这些示例所示,您需要稍微探索SimpleXML以了解它的魔力,以便您可以从中受益。

如果你喜欢魔法较少的界面,请结识姐妹DOMDocument

独立于SimpleXMLElement,只要你有Traversable(它也是如此),你可以使用一个名为iterator_count的函数来回馈迭代。对于SimpleXMLElement,它表示孩子的数量。