i want to parse xml files. Its for an importer, where you can define a configuration
My Problem is, that the xml parser (SimpleXml & Dom) did it inconstistence.
When i only have one child node - it will give me a simpleXML
<sizes>
<size>
<gpp> 5,00</gpp>
<gppcurrency>EUR</gppcurrency>
<npp> 5,00</npp>
<nppcurrency>EUR</nppcurrency>
<sp> 5,00</sp>
<spcurrency>EUR</spcurrency>
<stock>100</stock>
</size>
</sizes>
Will be
sizes [SimpleXmlElement]
=> size [SimpleXmlElement]
- gpp
- gppcurrency
...
BUT if i have multiple nodes
<sizes>
<size>
<gpp> 5,00</gpp>
<gppcurrency>EUR</gppcurrency>
<npp> 5,00</npp>
<nppcurrency>EUR</nppcurrency>
<sp> 5,00</sp>
<spcurrency>EUR</spcurrency>
<stock>100</stock>
</size>
<size>
<gpp> 5,00</gpp>
<gppcurrency>EUR</gppcurrency>
<npp> 5,00</npp>
<nppcurrency>EUR</nppcurrency>
<sp> 5,00</sp>
<spcurrency>EUR</spcurrency>
<stock>100</stock>
</size>
</sizes>
It will output
sizes [SimpleXmlElement]
=> size array
[0] [SimpleXmlElement]
- gpp
- gppcurrency
...
[1] [SimpleXmlElement]
- gpp
- gppcurrency
...
This is realy incosistent and may you can help me find an answer for this. Thanks
CodePudding user response:
Whether or not the internal pieces use an array does not matter. What is being returned is a SimpleXMLElement object (not a bare array) with getters that will return an iterable value in either case. The array you are seeing is how the value is stored within the SimpleXMLElement, but it will handle converting a single child item to an iterable value when you access it.
foreach($singleNode->size as $size) {
var_dump($size);
}
// object(SimpleXMLElement) { ["gpp"] ... }
foreach($multiNode->size as $size) {
var_dump($size);
}
// object(SimpleXMLElement) { ["gpp"] ... }
// object(SimpleXMLElement) { ["gpp"] ... }
CodePudding user response:
I have found my solution to consistencly get an array.
<?php
declare(strict_types=1);
namespace src\ImExport\Formatter;
use SimpleXMLElement;
class XmlToArrayFormatter
{
public function parseXmlToArray(SimpleXmlElement $xml, $collection = [])
{
$childNodes = $xml->children();
if (0 === $childNodes->count()) {
if (empty($xml)) {
return false;
}
return strval($xml);
}
$currentNodeAsArray = false;
if ($this->getNodeLevel($xml) > 0 &&
$this->hasOnlySubNodes($xml) &&
!is_string($xml)) {
$currentNodeAsArray = true;
}
foreach ($childNodes as $nodeName => $nodeValue) {
$xmlArray = $this->parseXmlToArray($nodeValue);
if ($currentNodeAsArray) {
$collection[$nodeName][] = $xmlArray;
} else {
if ($xmlArray !== false) {
$collection[$nodeName] = $xmlArray;
}
}
}
return $collection;
}
protected function getNodeLevel(?SimpleXMLElement $parentNodes, $level = 0): int
{
if (is_null($parentNodes)) {
return $level;
}
if (count($parentNodes->children())) {
$level ;
}
foreach ($parentNodes->children() as $parentNodeValues) {
$level = $this->getNodeLevel($parentNodeValues, $level);
}
return $level;
}
public function hasOnlySubNodes(?SimpleXMLElement $node): bool
{
foreach ($node->children() as $nodeValues) {
if (count($nodeValues->children()) == 0 && !empty(strval($nodeValues))) {
return false;
}
}
return true;
}
}
