Every example of xml iteration I've found online (including PHP docs, W3Schools, and a stackoverflow search) presumes that we know the structure ahead of time. I would like to create a loop that iterates as deep as it can go in every branch and simply returns the node names and values that it finds. For example:
<za-lord>
<orderid>dresden1234</orderid>
<customer>toot-toot</customer>
<pizza>
<sauce>marinara</sauce>
<crust>thin</crust>
<toppings>
<cheese>extra</cheese>
<veg>
<onions>yes</onions>
<peppers>extra</peppers>
<olives>no</olives>
</veg>
<meat>
<groundbeef>yes</groundbeef>
<ham>no</ham>
<sausage>no</sausage>
</meat>
</toppings>
</pizza>
</za-lord>
What I'm looking for, then, is:
orderid = dresden1234
customer = toot-toot
sauce = marinara
crust = thin
cheese = extra
onions = yes
peppers = extra
olives = no
groundbeef = yes
ham = no
sausage = no
I've spent a couple hours now writing code examples, testing different variations on foreach, and the short version is that nothing is getting me what I want. Not knowing the structure ahead of time, is it possible to recursively iterate the xml above and return node names and values using SimpleXML, and if so, how?
You can use a SimpleXMLIterator
object and recurse over it to get all the node values:
function list_nodes($sxi) {
for($sxi->rewind(); $sxi->valid(); $sxi->next() ) {
if ($sxi->hasChildren()) {
list_nodes($sxi->current());
}
else {
echo $sxi->key() . " = " . $sxi->current() . "
";
}
}
}
$sxi = new SimpleXMLIterator($xmlstr);
list_nodes($sxi);
Output:
orderid = dresden1234
customer = toot-toot
sauce = marinara
crust = thin
cheese = extra
onions = yes
peppers = extra
olives = no
groundbeef = yes
ham = no
sausage = no
Update
If your xml can have namespaces, you have to take a more complicated approach, checking each node for children in each of the namespaces in the document:
function list_children($node, $names) {
$children = false;
foreach ($names as $name) {
if (count($node->children($name))) {
$children = true;
foreach ($node->children($name) as $child) {
list_children($child, $names);
}
}
}
if (!$children) {
echo $node->getName() . " = $node
";
}
}
$xml = new SimpleXMLElement($xmlstr);
list_children($xml, array_merge(array(''), $xml->getNamespaces(true)));
Output (for the demo xml, same as the question but with namespaces added):
orderid = dresden1234
customer = toot-toot
sauce = marinara
crust = thin
cheese = extra
onions = yes
peppers = extra
olives = no
ham = no
sausage = no
groundbeef = yes