I'm fetching some xml and convert it to csv similar to below. Some of the records have additional n (unbounded) elements ("EntityEvents"). How can I fetch them as well and write them into a second (mm) csv file?
This is my structure:
XML File:
<abc:ABCData xmlns:abc="http://www.abc-example.com" xmlns:xyz="http:/www.xyz-example.com">
<abc:ABCRecords>
<abc:ABCRecord>
<abc:ABC>5EXZX4LPK</abc:ABC>
<abc:Entity>
<abc:Name>Bornheim</abc:Name>
<abc:EntityEvents>
<abc:EntityEvent>EventA</abc:Event>
</abc:EntityEvents>
</abc:Entity>
</abc:ABCRecord>
<abc:ABCRecord>
<abc:ABC>5967007LI</abc:ABC>
<abc:Entity>
<abc:Name>MOON BANK</abc:Name>
<abc:EntityEvents>
<abc:EntityEvent>EventB</abc:Event>
<abc:EntityEvent>EventC</abc:Event>
</abc:EntityEvents>
</abc:Entity>
</abc:ABCRecord>
<abc:ABCRecord>
<abc:ABC>2792340TZ</abc:ABC>
<abc:Entity>
<abc:Name>SUN BANK</abc:Name>
<abc:EntityEvents>
<abc:EntityEvent>EventD</abc:Event>
<abc:EntityEvent>EventF</abc:Event>
<abc:EntityEvent>EventG</abc:Event>
</abc:EntityEvents>
</abc:Entity>
</abc:ABCRecord>
</abc:ABCRecords>
</abc:ABCData>
PHP file:
<?php
$reader = new XMLReader();
$reader->open('php://stdin');
$output = fopen('php://stdout', 'w');
fputcsv($output, ['id', 'name']);
$xmlns = [
'abc' => 'http://www.abc-example.com'
];
$dom = new DOMDocument;
$xpath = new DOMXpath($dom);
foreach ($xmlns as $prefix => $namespaceURI) {
$xpath->registerNamespace($prefix, $namespaceURI);
}
while (
$reader->read() &&
(
$reader->localName !== 'ABCRecord' ||
$reader->namespaceURI !== $xmlns['abc']
)
) {
continue;
}
while ($reader->localName === 'ABCRecord') {
if ($reader->namespaceURI === 'http://www.abc-example.com') {
$node = $reader->expand($dom);
fputcsv(
$output,
[
$xpath->evaluate('string(abc:ABC)', $node),
$xpath->evaluate('string(abc:Entity/abc:Name)', $node)
]
);
}
$reader->next('ABCRecord');
}
Output 1 (CSV):
5EXZX4LPK,Bornheim
5967007LI,"MOON BANK"
2792340TZ,"SUN BANK"
Desired Output 2 (CSV):
5EXZX4LPK,EventA
5967007LI,EventB
5967007LI,EventC
2792340TZ,EventD
2792340TZ,EventE
2792340TZ,EventF
How can I accomplish this? I thought of writing them into a separate file but I'm open how to accomplish this. I'm also open to do it in two steps, meaning in a separate php file.
CodePudding user response:
Open a secondary file handle. Then after expanding the node into DOM, use an expression to fetch the events and write them to the second file.
//...
$node = $reader->expand($dom);
// store the identifier
$identifier = $xpath->evaluate('string(abc:ABC)', $node);
fputcsv(
$output,
[
$identifier,
$xpath->evaluate('string(abc:Entity/abc:Name)', $node)
]
);
// iterate the EntityEvent elements
foreach ($xpath->evaluate('abc:Entity/abc:EntityEvents/abc:EntityEvent', $node) as $event) {
fputcsv(
$detailOutput,
[
$identifier,
$event->textContent
]
);
}
//...
CodePudding user response:
Using a single XPath expression and tag names to fill csv arrays
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$csv1 = [];
$csv2 = [];
foreach ($xpath->evaluate("(//abc:ABCRecord/abc:ABC | //abc:Entity/abc:Name) | (//abc:ABCRecord/abc:ABC | //abc:ABCRecord//abc:EntityEvent)") as $ele) {
if($ele -> localName == 'ABC'){
$n = $ele->nodeValue;
}
if($ele -> localName == 'Name'){
$csv1[] = $n . ','. $ele -> nodeValue;
}else if($ele -> localName == 'EntityEvent'){
$csv2[] = $n . ','. $ele -> nodeValue;
}
}
var_dump($csv1);
var_dump($csv2);
Result
array(3) {
[0]=>
string(18) "5EXZX4LPK,Bornheim"
[1]=>
string(19) "5967007LI,MOON BANK"
[2]=>
string(18) "2792340TZ,SUN BANK"
}
array(6) {
[0]=>
string(16) "5EXZX4LPK,EventA"
[1]=>
string(16) "5967007LI,EventB"
[2]=>
string(16) "5967007LI,EventC"
[3]=>
string(16) "2792340TZ,EventD"
[4]=>
string(16) "2792340TZ,EventF"
[5]=>
string(16) "2792340TZ,EventG"
}