2

There's lots of these questions, so forgive me. I've read them all.

I have the following XML document using the namespace http://www.columbasystems.com/cpng/xmlviewer/cal/1-0/ : http://events.manchester.ac.uk/f3vf/calendar/tag:manchester_museum/view:list/p:q_details/calml.xml

I'm attempting to parse this document using SimpleXML. The example code below is an attempt at accessing the value of the title node "Discovery Center" from the below.

<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'));
    }
}

OUTPUTS several empty arrays:

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

I think i'm using xpath wrong, how do i get to the values of nodes like these?

3
  • What happens when your code runs? Commented Jul 8, 2015 at 16:42
  • It dumps an empty array 47 times. Nested within the first instance of <ns:day> there are 5 individual event elements so the nested foreach is clearly not working as i'd expect. Output was : array ( 0 => SimpleXMLElement::__set_state(array( )), and so on. edit: updated question to include output sample Commented Jul 8, 2015 at 16:46
  • @digitalpencil look update in my answer Commented Jul 9, 2015 at 10:18

2 Answers 2

4

Your output is not an empty array. An empty array looks like this:

array()

But you have this:

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

So, clearly the XPath is working, and giving you a list (array) of results (SimpleXMLElement objects).

The problem is that var_export isn't very good at inspecting SimpleXMLElement objects, so you can't see what result you actually got.

To get at the text content of a node, you have to cast it to a string - explicitly with (string)$node, or implicitly with something like echo. So the below will work:

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

However, you do have a small bug in your XPath expressions (irrelevant of if you used SimpleXML or any other API): the // prefix always starts at the root of the document, not the element being used as context. To search for "any depth within the current element" you need .//, e.g. $day->xpath('.//ns:event').

That said, you don't actually need anything as fancy as XPath here at all, because it's not that deep a structure. So you can just use SimpleXML's normal access methods, as long as you first select the right namespace using the ->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;
    }
}

Note that your XML contains attributes without namespace prefixes like <ns:day date="2015-07-09" weekDay="Thu">; somewhat unintuitively, these are officially in no namespace at all, so you have to switch back to the null namespace to access them:

echo $day->attributes(null)['date'];
Sign up to request clarification or add additional context in comments.

Comments

2

I placed xml in string, change back to file. I hope, other is clear

$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];

    }
}

result

2015-07-08 Discovery Centre

UPDATE You can use prefix insead full url. And take in mind how get attrbute value in this case

$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;
    }
}

7 Comments

To be clear, the only thing actually making a difference here is using echo rather than var_export. The /text() on the end of the XPath expression is actually ignored by SimpleXML, because it has no object type for a text node; the text content is extracted by using a string cast ((string)$simplexml_element) which echo does implicitly.
@IMSoP I agree about text - it is my usual problem, coming from xpath testers :) But, about xpath and direct adreesing, i don't belive OP about correct sorce data :) You right if it as in question. But OP use xpath on all levels
Not sure what you mean by "correct source data"; the URL is in the question, and it looks just as the example implies. I think the OP is using xpath because they don't know they don't need to.
Your update is incorrect. The ns in ->children("ns", true) has nothing to do with registering the XPath namespace, it just takes whatever prefix happened to be assigned in the source XML. You're right on needing ->attributes() though (to switch back to the empty-named namespace).
I tested. you are right. I thought that prefix must be from registerXPathNamespace
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.