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;
}
}