【问题标题】:Accessing XML node value using SimpleXML and xpath使用 SimpleXML 和 xpath 访问 XML 节点值
【发布时间】:2015-07-08 16:40:25
【问题描述】:

这些问题很多,请见谅。我都读过了。

我有以下使用命名空间 http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/ 的 XML 文档:http://events.manchester.ac.uk/f3vf/calendar/tag:manchester_museum/view:list/p:q_details/calml.xml

我正在尝试使用 SimpleXML 解析此文档。下面的示例代码是尝试从下面访问标题节点“发现中心”的值。

<ns:calendar xmlns:ns="http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/">
    <ns:listView>
    <ns:day date="2015-07-08" weekDay="Wed">
    <ns:event>
    <ns:id xmlns:even="http://www.columbasystems.com/customers/uom/gpp/eventid/" query="{http://www.columbasystems.com/customers/uom/gpp/eventid/}b9v-ib270yqf-nmn54k">even:b9v-ib270yqf-nmn54k</ns:id>
    <ns:title>Discovery Centre</ns:title>
    ...
</ns:event>
</ns:day>
</ns:listView>
</ns:calendar>

PHP:

$feed_uri = 'http://events.manchester.ac.uk/f3vf/calendar/tag:manchester_museum/view:list/p:q_details/calml.xml';
$xml = simplexml_load_file($feed_uri);
$xml->registerXPathNamespace("ns", "http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/");

foreach($xml->xpath('//ns:calendar/ns:listView/ns:day') as $day) {
    $events = $day->xpath('//ns:event');
    foreach($events as $event) {
        var_export($event->xpath('//ns:title'));
    }
}

输出几个空数组:

array ( 0 => SimpleXMLElement::__set_state(array( )),

我认为我使用 xpath 错误,我如何获得这些节点的值?

【问题讨论】:

  • 代码运行时会发生什么?
  • 它转储一个空数组 47 次。嵌套在 的第一个实例中,有 5 个单独的事件元素,因此嵌套的 foreach 显然没有像我预期的那样工作。输出为:array ( 0 => SimpleXMLElement::__set_state(array( )) 等等。编辑:更新问题以包含输出样本
  • @digitalpencil 在我的回答中查看更新

标签: php xml simplexml


【解决方案1】:

您的输出不是空数组。一个空数组如下所示:

array()

但你有这个:

array ( 0 => SimpleXMLElement::__set_state(array( )),

因此,显然 XPath 正在工作,并为您提供结果列表(数组)(SimpleXMLElement 对象)。

问题是var_export 不太擅长检查 SimpleXMLElement 对象,因此您看不到实际得到的结果。

要获取节点的文本内容,您必须将其转换为字符串——显式使用(string)$node,或隐式使用echo。所以下面会起作用:

foreach($xml->xpath('//ns:calendar/ns:listView/ns:day') as $day) {
    $events = $day->xpath('//ns:event');
    foreach($events as $event) {
        foreach ($event->xpath('//ns:title') as $title ) {
            echo $title;
        }
    }
}

但是,您的 XPath 表达式中确实存在一个小错误(与您是否使用 SimpleXML 或任何其他 API 无关):// 前缀始终从文档的根开始,而不是从用作上下文的元素开始。要搜索“当前元素内的任何深度”,您需要.//,例如$day-&gt;xpath('.//ns:event').

也就是说,您实际上根本不需要像 XPath 这样花哨的东西,因为它不是那么深的结构。所以你可以直接使用SimpleXML's normal access methods,只要你先用the -&gt;children() method选择正确的命名空间:

$cal_items = $xml->children("http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/");

foreach($cal_items->listView->day as $day) {
    foreach($day->event as $event) {
        echo $event->title;
    }
}

请注意,您的 XML 包含没有命名空间前缀的属性,例如 &lt;ns:day date="2015-07-09" weekDay="Thu"&gt;;有点不直观,这些是 officially in no namespace at all,所以你必须切换回 null 命名空间才能访问它们:

echo $day->attributes(null)['date'];

【讨论】:

    【解决方案2】:

    我将 xml 放在字符串中,改回文件。我希望,其他的很清楚

    $str = '<ns:calendar xmlns:ns="http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/">
        <ns:listView>
        <ns:day date="2015-07-08" weekDay="Wed">
        <ns:event>
        <ns:id xmlns:even="http://www.columbasystems.com/customers/uom/gpp/eventid/" query="{http://www.columbasystems.com/customers/uom/gpp/eventid/}b9v-ib270yqf-nmn54k">even:b9v-ib270yqf-nmn54k</ns:id>
        <ns:title>Discovery Centre</ns:title>
    </ns:event>
    </ns:day>
    </ns:listView>
    </ns:calendar>';
    
    $xml = simplexml_load_string($str);
    $xml->registerXPathNamespace("ns", "http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/");
    
    foreach($xml->xpath('//ns:calendar/ns:listView/ns:day') as $day) {
        echo $day['date'] . ' ';
        $events = $day->xpath('.//ns:event');
        foreach($events as $event) {
            echo $event->xpath('.//ns:title/text()')[0];
    
        }
    }
    

    结果

    2015-07-08 Discovery Centre
    

    更新 您可以使用前缀 insead 完整网址。并记住在这种情况下如何获得属性值

    $cal_items = $xml->children("ns",true);
    foreach($cal_items->listView->day as $day) {
        echo $day->attributes()['date'] . ' ' ;
        foreach($day->event as $event) {
            echo $event->title;
        }
    }
    

    【讨论】:

    • 需要明确的是,这里唯一真正有所作为的是使用echo 而不是var_export。 XPath 表达式末尾的/text() 实际上被 SimpleXML 忽略了,因为它没有文本节点的对象类型;文本内容是通过使用 echo 隐式执行的字符串转换 ((string)$simplexml_element) 提取的。
    • @IMSoP 我同意text - 这是我经常遇到的问题,来自 xpath 测试人员 :) 但是,关于 xpath 和直接访问,我不相信 OP 关于正确的源数据 :) 你对,如果它有问题。但是 OP 在所有级别上都使用 xpath
    • 不确定“正确的源数据”是什么意思; URL 在问题中,它看起来就像示例所暗示的那样。我认为 OP 正在使用 xpath,因为他们不知道他们不需要。
    • 您的更新不正确。 -&gt;children("ns", true) 中的 ns 与注册 XPath 命名空间无关,它只需要在源 XML 中分配的任何前缀。您需要 -&gt;attributes() 是对的(切换回空名称空间)。
    • 我测试过。你说的对。我认为前缀必须来自registerXPathNamespace
    猜你喜欢
    • 2012-11-30
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多