Home > Mobile >  PHP - Fetching xml values with looping over n unbounded element
PHP - Fetching xml values with looping over n unbounded element

Time:04-23

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"
}
  • Related