chross
chross

Reputation: 561

SimpleXML access nodes with namespace and subnodes without namespace

I am trying to access a list of nodes without namespace declaration within nodes with namespace declaration. My XML file has a main node with namespace ehd with two subnodes header, body within the same namespace. However, all subnodes within the body node have no further namespace declaration. I am struggling with accessing these nodes with SimpleXML.

Excerpt from the xml file:

<?xml version="1.0" encoding="ISO-8859-15"?>
<ehd:ehd ehd_version="1.40" xmlns:ehd="urn:ehd/001" xmlns="urn:ehd/go/001">
    <ehd:header>
    </ehd:header>
    <ehd:body>
        <gnr_liste>
            <gnr V="01100"></gnr>
            <gnr V="01101"></gnr>
            <gnr V="01102"></gnr>
        </gnr_liste>
</ehd:body>
</ehd:ehd>

My code is as follows:

$xml = simplexml_load_file($file) or die("Failed to load");   
    $ehd = $xml->children('ehd', true)->body;
    simplexml_dump($ehd);
    $gnr_liste = $ehd->children('gnr_liste')->children('gnr');
    simplexml_dump($gnr_liste);

The output is:

SimpleXML object (1 item)
[
    Element {
        Namespace: 'urn:ehd/001'
        Namespace Alias: 'ehd'
        Name: 'ehd'
        String Content: ''
        Content in Namespace ehd
            Namespace URI: 'urn:ehd/001'
            Children: 2 - 1 'body', 1 'header'
            Attributes: 0
        Content in Default Namespace
            Children: 0
            Attributes: 1 - 'ehd_version'
    }
]
SimpleXML object (1 item)
[
    Element {
        Namespace: 'urn:ehd/001'
        Namespace Alias: 'ehd'
        Name: 'body'
        String Content: ''
        Content in Default Namespace
            Namespace URI: 'urn:ehd/go/001'
            Children: 1 - 1 'gnr_liste'
            Attributes: 0
    }
]

How do I access all gnr items from the gnr_liste node?

Note: I am using simplexml_dump for debugging

Upvotes: 2

Views: 606

Answers (2)

IMSoP
IMSoP

Reputation: 97718

The argument to ->children() is always a namespace identifier or local prefix, never the tag name. If these elements were in "no namespace", you would access them with ->children('').

However, the elements with no prefix in this document do not have no namespace - they are in the default namespace, in this case urn:ehd/go/001 (as defined by xmlns="urn:ehd/go/001").

If you use the full namespace identifiers rather than the prefixes (which is also less likely to break if the feed changes), you should be able to access these easily:

$xml = simplexml_load_file($file) or die("Failed to load");   
$ehd = $xml->children('urn:ehd/001')->body;
$gnr_liste = $ehd->children('urn:ehd/go/001')->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
    simplexml_dump($gnr);
}

You might want to give your own names to the namespaces so you don't have to use the full URIs, but aren't dependent on the prefixes the XML is generated with; a common approach is to define constants:

const XMLNS_EHD_MAIN = 'urn:ehd/001';
const XMLNS_EHD_GNR = 'urn:ehd/go/001';

$xml = simplexml_load_file($file) or die("Failed to load");   
$ehd = $xml->children(XMLNS_EHD_MAIN)->body;
$gnr_liste = $ehd->children(XMLNS_EHD_GNR)->gnr_liste;
foreach ( $gnr_liste->gnr as $gnr ) {
    simplexml_dump($gnr);
}

Upvotes: 3

miken32
miken32

Reputation: 42715

Personally, I find DomDocument much more intuitive to work with – once you get over the barrier of XPath syntax. No matter what tool you use, namespaces are going to make everything more difficult though!

$xml = <<< XML
<?xml version="1.0" encoding="ISO-8859-15"?>
<ehd:ehd ehd_version="1.40" xmlns:ehd="urn:ehd/001" xmlns="urn:ehd/go/001">
    <ehd:header>
    </ehd:header>
    <ehd:body>
        <gnr_liste>
            <gnr V="01100"></gnr>
            <gnr V="01101"></gnr>
            <gnr V="01102"></gnr>
        </gnr_liste>
</ehd:body>
</ehd:ehd>
XML;

$dom = new DomDocument;
$dom->loadXML($xml);
$xp = new DomXPath($dom);
// need to get tricky due to namespaces https://stackoverflow.com/a/16719351/1255289
$nodes = $xp->query("//*[local-name()='gnr']/@V");
foreach ($nodes as $node) {
    printf("%s\n", $node->value);
}

Output:

01100
01101
01102

Upvotes: 2

Related Questions