Reputation: 1122
An XML file with varying depths of 'children' in a family needs to be parsed to get all the descendents into a list.
As shown in the example code at https://3v4l.org/GV0UV , which includes the XML, the code that processes the XML is this, where $buffer
contains the XML:
$xml = new SimpleXMLIterator($buffer);
$recursive = new RecursiveIteratorIterator($xml);
foreach ($recursive as $tag => $object) {
if ($tag !== 'Name') {
continue;
}
echo "-> " . $object ;
}
The output is this
-> 1David-> 1Samuel-> 1Fred-> 1John-> 1Frank-> 1Robert-> 2David-> 2Samuel-> 2Fred-> 2John-> 3David-> 3Samuel-> 3Fred-> 3John-> 3Frank
I need to separate this into families ('1David' is a person in a different family than '2David'; in real world, assume different names for each family member).
The desired output should be like this:
-> 1David-> 1Samuel-> 1Fred-> 1John-> 1Frank-> 1Robert
-> 2David-> 2Samuel-> 2Fred-> 2John
-> 3David-> 3Samuel-> 3Fred-> 3John-> 3Frank
So the RecursiveIteratorIterator
is finding all of the 'name' values in the XML, but the output needs to be separated into 'families'.
How do I separate the extraction of the names into families?
(Note: my previous question is here Iterating through a multi-level XML file ; it was suggested that I start a new question.)
Upvotes: 1
Views: 61
Reputation: 19492
Here is a different approach for this using Xpath expressions. It is not recursive, but a lot more specific. Xpath has axis like ancestor
and descendant
to make the expression go in a specific direction on the node tree.
$document = new DOMDocument();
$document->loadXml($buffer);
$xpath = new DOMXpath($document);
// for each top level Family
foreach ($xpath->evaluate('/root/Family') as $family) {
echo $xpath->evaluate('string(Name)', $family), "\n-------------------\n";
// find all ancestors without ancestors themself
foreach ($xpath->evaluate('.//Ancestors[not(.//Ancestors)]/Family', $family) as $ancestor)
$names = [];
// get the Family nodes on the path upwards
foreach ($xpath->evaluate('ancestor::Family', $ancestor) as $descendant) {
// collect the name
$names[] = $xpath->evaluate('string(Name)', $descendant);
}
echo implode(' -> ', $names), "\n";
}
echo "\n";
}
Output:
1David
-------------------
1David -> 1Samuel -> 1Fred -> 1John -> 1Frank
2David
-------------------
2David -> 2Samuel -> 2Fred
3David
-------------------
3David -> 3Samuel -> 3Fred -> 3John
Upvotes: 0
Reputation: 57121
You could simply check the level of the RecursiveIteratorIterator
(using getDepth()
) which will tell you how far into the nesting you are. 1 means you are at the top level...
foreach ($recursive as $tag => $object) {
if ($tag !== 'Name') {
continue;
}
if ( $recursive->getDepth() == 1 ) {
echo PHP_EOL;
}
echo "-> " . $object ;
}
which will echo out...
-> 1David-> 1Samuel-> 1Fred-> 1John-> 1Frank-> 1Robert
-> 2David-> 2Samuel-> 2Fred-> 2John
-> 3David-> 3Samuel-> 3Fred-> 3John-> 3Frank
Although it does have a blank line at the front, this should be easy to remove.
To turn this into something which stores the data...
$output = [];
$family = [];
foreach ($recursive as $tag => $object) {
if ($tag !== 'Name') {
continue;
}
if ( $recursive->getDepth() == 1 ) {
if (!empty($family) ) {
$output[] = $family;
$family = [];
}
}
$family[] = (string)$object;
}
if (!empty($family) ) {
$output[] = $family;
}
print_r($output);
gives...
Array
(
[0] => Array
(
[0] => 1David
[1] => 1Samuel
[2] => 1Fred
[3] => 1John
[4] => 1Frank
[5] => 1Robert
)
[1] => Array
(
[0] => 2David
[1] => 2Samuel
[2] => 2Fred
[3] => 2John
)
[2] => Array
(
[0] => 3David
[1] => 3Samuel
[2] => 3Fred
[3] => 3John
[4] => 3Frank
)
)
Upvotes: 3