【问题标题】:Parsing XML with missing nodes解析缺少节点的 XML
【发布时间】:2013-06-24 10:21:01
【问题描述】:

我有一个包含订单的 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>

一个文件中有许多 &lt;order&gt; 节点。一切都是正确的,但突然间我收到了没有任何付款信息的订单

<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>

注意空的&lt;payments/&gt; 节点。 我的 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 并且显然停止解析文件的其余部分。
我希望我的脚本停止并继续解析文件的其余部分。我不想让你“闭嘴”@ 运营商,但我看不到任何其他方式。

【问题讨论】:

  • 在访问payment 的成员之前,你为什么不测试payments 是否是一个对象?
  • 我将该文件中的数据保存在数据库中,我检查了大约 40 个节点。您是否建议使用property_exists 添加一些功能?
  • 我正在回答这个具体案例。如果你有很多代码处理这个并且多个节点可能会丢失,你可以查看php.net/manual/en/class.simplexmliterator.php。如果您的 XML 可能“不完整”,我不确定为什么在访问每个级别之前不检查它。你有没有尝试捕捉这个特殊的异常?

标签: php xml simplexml


【解决方案1】:

您需要添加逻辑以确保&lt;payments&gt; 具有支付数据(即payment 节点)。

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

更新

尽管上面的 docs claim 应该适用于 PHP > 5.3,但在 OP 的情况下它返回了 1(不知道为什么)。

最终需要以下内容:

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

【讨论】:

  • $order-&gt;payments-&gt;count() 返回 1 但&lt;payments/&gt; 没有孩子?
  • 你运行的是什么版本的 PHP?
  • PHP 版本 5.3.1 根据文档应该没问题(PHP 5 &gt;= 5.3.0)
  • 刚刚发现 $order->payments->count() 返回 1 但 $order->payments[0]->count() 返回 0
  • 我确信有一个简单的解释。我不像你那么接近代码。
【解决方案2】:

SimpleXML 带有一些魔法。让我们看看您如何访问(现有或不存在的)&lt;payment&gt; 元素:

$order->payments->payment

无论&lt;payments&gt; 元素中的&lt;payment&gt; 元素的计数是否为零或更大,这将(总是!)产生SimpleXMLElement

但是,如果计数实际上为零,则访问第一个 &lt;payment&gt; 元素将返回 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() 方法。它计算一个元素的子元素并且它也可以工作,但是另一个答案建议使用它的位置错误,正确的位置是 &lt;payment&gt; 元素,而不是 &lt;payments&gt; 元素:

$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,它表示孩子的数量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-05
    相关资源
    最近更新 更多