I struggled for the last couple days on this question and finally came up with a solution. But it's got so many loops I get dizzy just looking at the code - from recursive functions with loops to loops within loops.
I really expected to find a canned function in a popular XML class to extract all the unique nodes without data, but I am surprised how non-trivial I've found this endeavor.
While this works for my piddly example data, I have performance and scalability concerns when trying to use this code in production. For example, this elegant answer uses XSLT to solve the problem, but it's limited by node depth. So is there anyway to improve efficiency by cutting out a loop, or using less objects, or even utilizing a different class or technology completely? Are there any glaring failing conditions that aren't represented in my example data?
<?php
$xml = '
<root>
<node/>
<node>
<sub>more</sub>
</node>
<node>
<sub>another</sub>
</node>
<node>value</node>
</root>
';
$doc = new DOMDocument();
$doc->loadXML($xml);
// clone without data
$empty_xml = new DOMDocument();
$empty_xml->appendChild($empty_xml->importNode($doc->documentElement));
function clone_without_data(&$orig, &$clone, &$clonedoc){
foreach ($orig->childNodes as $child){
if(get_class($child) === "DOMElement")
$new_node = $clone->appendChild($clonedoc->importNode($child));
if($child->hasChildNodes())
clone_without_data($child,$new_node,$clonedoc);
}
}
clone_without_data($doc->documentElement, $empty_xml->documentElement, $empty_xml);
// remove all duplicates
$distinct_structure = new DOMDocument();
$distinct_structure->appendChild($distinct_structure->importNode($doc->documentElement));
foreach ($empty_xml->documentElement->childNodes as $child){
$match = false;
foreach ($distinct_structure->documentElement->childNodes as $element){
if ($distinct_structure->saveXML($element) === $empty_xml->saveXML($child)) {
$match = true;
break;
}
}
if (!$match)
$distinct_structure->documentElement->appendChild($distinct_structure->importNode($child,true));
}
$distinct_structure->formatOutput = true;
echo $distinct_structure->saveXML();
This results in the unique XML structures stripped of all data:
<?xml version="1.0"?>
<root>
<node/>
<node>
<sub/>
</node>
</root>
$xml = '...
\$\endgroup\$