Reputation: 561
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
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
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