58

I have this as xml:

<root xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
    <event:event>
        <event:sessionKey></event:sessionKey>
        <event:sessionName>Learn QB in Minutes</event:sessionName>
        <event:sessionType>9</event:sessionType>
        <event:hostWebExID></event:hostWebExID>
        <event:startDate>02/12/2009</event:startDate>
        <event:endDate>02/12/2009</event:endDate>
        <event:timeZoneID>11</event:timeZoneID>
        <event:duration>30</event:duration>
        <event:description></event:description>
        <event:status>NOT_INPROGRESS</event:status>
        <event:panelists></event:panelists>
        <event:listStatus>PUBLIC</event:listStatus>
    </event:event>
    ...
</root>

How can I loop through all of the event:event nodes and display, for example, all of the event:SessionKey's?

This does not work:

$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');

foreach($xml->xpath('//e:event') as $event) {
 var_export($event->xpath('//e:sessionKey'));
}
hakre
  • 193,403
  • 52
  • 435
  • 836
user38968
  • 661
  • 1
  • 8
  • 8
  • 3
    This might help http://www.lornajane.net/posts/2010/Fetching-Namespaced-XML-Elements-With-SimpleXML – Ajinkya Kulkarni Nov 30 '10 at 23:33
  • 1
    *does not work* is and never was a proper error description. PHP gives you the following error: *"Warning: SimpleXMLElement::xpath(): Undefined namespace prefix"*. If you develop software, you should listen to PHP's warnings. See as well: [How to get useful error messages in PHP?](http://stackoverflow.com/q/845021/367456) – hakre Nov 23 '13 at 14:32
  • See also [Reference - how do I handle namespaces (tags and attributes with colon in) in SimpleXML?](https://stackoverflow.com/questions/44894426/reference-how-do-i-handle-namespaces-tags-and-attributes-with-colon-in-in-si) – IMSoP Apr 28 '21 at 14:24

6 Answers6

32

it does work without registerXPathNamespace and the full namespace prefix in the xpath queries:

$xml = new SimpleXMLElement($r);

foreach($xml->xpath('//event:event') as $event) {
 var_export($event->xpath('event:sessionKey'));
}
ax.
  • 58,560
  • 8
  • 81
  • 72
  • 10
    Yes and no, actually I consider this a bug. PHP automatically registers the namespaces of the current context. This even overrides namespaces you register yourself. You base your source on the prefixes used in the document your loading. An external source you have no control over. Prefixes that are optional, ambiguous and can change any time. Namespaces are defined, unique and stable - prefixes/aliases are not. – ThW Apr 16 '15 at 09:41
23

You have to register the namespace for each simpleXMLElement object you use.

$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');

foreach($xml->xpath('//e:event') as $event) {
    $event->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
    var_export($event->xpath('//e:sessionKey'));
}

The namespace should also be declared somewhere in the xml file.

<event:event xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
...

The method ax described works too. You can skip the registerXPathNamespace if you know the xml file will always use the same prefix.

mcrumley
  • 5,682
  • 3
  • 25
  • 33
19

here alternative that worked for me.

$xml = simplexml_load_string($r);
$ns = $xml->getNamespaces(true);

foreach ($xml->children($ns['event'])->event as $skey) {
    $sessionKey = $skey->children($ns['event'])->sessionKey;
    echo $sessionKey;
}
ewwink
  • 18,382
  • 2
  • 44
  • 54
  • The trick is using `children()` with at least its first parameter. If you know the document structure before hand, you can even type the namespace name (or its URI) manually as an alternative to `getNamespaces()`. – Álvaro González Feb 11 '14 at 16:23
  • For easier typing trick is to cast to object, ie `$ns = (object) $xml->getNamespaces(true);` then You could use this like `$sessionKey = $skey->children($ns->event)->sessionKey;` – PeterM Oct 08 '18 at 11:05
19

Having worked a lot with simplexml, this is how I do it.

The magic trick if you already have an element and just want to get its different namespaced children, say for a structure like this:

<entry>
<title type="text">My test entry</title>
<gd:when startTime="2017-02-26T02:00:00Z" endTime="2017-02-26T03:00:00Z"/>
<gc:notes type="string">A type</gc:notes>
</entry>

Is to send TRUE as the second parameter to the children function:

  $title = (string) $entry->title;
  $gd = $entry->children('gd', TRUE);
  $attrs = $gd->when->attributes();
  $startTime = (string) $attrs->startTime;
  $gc = $entry->children('gc', TRUE);
  $notes = (string) $gc->notes();
Cameron
  • 995
  • 10
  • 16
8

Another approach is to use SimpleXML for parsing and DOMDocument for manipulation/access, which bypasses namespacing issues altogether:

$xml = new SimpleXMLElement($r);
$xml = dom_import_simplexml($xml);
$nodelist= $xml->getElementsByTagName('event');  
for($i = 0; $i < $nodelist->length; $i++) {
    $sessions = $nodelist->item($i)->getElementsByTagName('sessionKey');
    echo $sessions->item(0)->nodeValue;
}
Asad Saeeduddin
  • 46,193
  • 6
  • 90
  • 139
5

Using registerXPathNamespace and then calling xpath didn't actually work for me. I had to go with the solution provided in this great post : http://blog.preinheimer.com/index.php?/archives/172-SimpleXML,-Namespaces-Hair-loss.html

So in your case, this :

echo $xml->children('http://www.webex.com/schemas/2002/06/service/event')->sessionName;

Will output:

Learn QB in Minutes

Raf
  • 1,083
  • 11
  • 15