Home > Back-end >  PHP XML Parsing Problem with single node - inconsistence
PHP XML Parsing Problem with single node - inconsistence

Time:10-17

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