75buy4b68737887
75buy4b68737887

Reputation: 11

SimpleXml with namespace issues

I have tried almost everything but i cannot seem to read the namespaced "m:inline" element and the "feed" and "title" children from the following SimpleXMLElement dump:

SimpleXMLElement {#235
+"@attributes": array:4 [
    "rel" => "http://schemas.microsoft.com/ado/2007/08/dataservices/related/test"
    "type" => "application/atom+xml;type=feed"
    "title" => "some title"
    "href" => "JobRequisition(23453453L)/Test"
]
+"m:inline": SimpleXMLElement {#152
  +"feed": SimpleXMLElement {#123
    +"title": "jobReqLocale"

...some more data ahead skipped here

The raw xml start:

<link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/jobReqLocale" type="application/atom+xml;type=feed" title="some title" href="JobRequisition(23453453L)/Test">
        <m:inline>
            <feed>
                <title type="text">jobReqLocale</title>

...

I have tried:

$simpleXmlElement = new SimpleXMLElement($xml, LIBXML_NOERROR, false);

dd($simpleXmlElement->children('m', true)->feed);

The result is always an empty SimpleXmlElement

with all kinds of variations including xpath. Am i doing something wrong? Is SimpleXML not supporting these kinds of structures?

Upvotes: 1

Views: 881

Answers (1)

IMSoP
IMSoP

Reputation: 97783

Your document contains (at least) two namespaces: one with the local prefix m, and one with no prefix (the "default namespace"). It may actually contain other namespaces you haven't shown, or re-assign both the m: prefix and the default namespace at different points in the document, but within the fragment you've shown:

  • The inline element is in the namespace with local prefix m
  • The link, feed, and title elements are in the default namespace
  • The various attributes are in no namespace at all; peculiarly, this isn't the same as them being in the default namespace.

As discussed on "Reference - how do I handle namespaces (tags and attributes with colon in) in SimpleXML?" the ->children() method switches namespaces, so:

  • To select the inline element, you would use ->children('m', true)->inline
  • To select the feed element inside it, you need to switch back to the default namespace, with ->children(null, true)->feed
  • To access the title element inside that, you will already be in the default namespace, so can just use ->title
  • In this particular case, it doesn't matter if you are in the default namespace or "no namespace", so the type attribute can be accessed directly with ['type']
  • Remember to cast any attribute or element to string to get its string content

To summarise:

$type = (string)$simpleXmlElement
    ->children('m', true)
            ->inline
    ->children(null, true)
            ->feed
            ->title
            ['type'];

Note that ideally you'd hard-code the namespace URIs, which are not subject to change, rather than the prefixes. You could also avoid assuming which is the default namespace and explicitly select the null namespace when selecting attributes. That would give you something more like this:

$type = (string)$simpleXmlElement
    ->children(XMLNS_SOMETHING)
            ->inline
    ->children(XMLNS_OTHER_THING)
            ->feed
            ->title
    ->attributes(null)
            ->type;

Upvotes: 1

Related Questions