phpxml

PHP - convert XML to array with all attributes


Here is my XML code:

<?xml version="1.0" encoding="UTF-8"?>
<file>
  <header>
    <information>Example information</information>
    <agency>1@test.uk</agency>
    <date>2025-03-19 16:13:44</date>
    <version>0.4</version>
    <path>mail@mail.uk</path>
    <value>full</value>
  </header>
  <offers_list>
    <department tab="local" type="sell">
      <offert>
        <param name="id">445/443/TS</param>
        <location>
          <area level="1">UK</area>
          <area level="2">ON</area>
          <area level="3">PIN</area>
          <area level="4">As</area>
          <area level="5">Ord</area>
        </location>
        <price currency="USD">1059000.00</price>
        <n_geo_y>51.1894791</n_geo_y>
        <n_geo_x>19.9072292</n_geo_x>
        <street>Example</street>
      </offert>
    </department>
  </offers_list>
</file>

and I'm trying to convert this XML text to array in that way:

    $xml_string = file_get_contents($path.'file_xml.xml');
    $xml = simplexml_load_string($xml_string, null, LIBXML_NOCDATA);
    $xml = json_decode(json_encode($xml), true);
    var_dump($xml);

Result:

array(2) {
  ["header"]=>
  array(6) {
    ["information"]=>
    string(19) "Example information"
    ["agency"]=>
    string(9) "1@test.uk"
    ["date"]=>
    string(19) "2025-03-19 16:13:44"
    ["version"]=>
    string(3) "0.4"
    ["path"]=>
    string(12) "mail@mail.uk"
    ["value"]=>
    string(4) "full"
  }
  ["offers_list"]=>
  array(1) {
    ["department"]=>
    array(2) {
      ["@attributes"]=>
      array(2) {
        ["tab"]=>
        string(5) "local"
        ["type"]=>
        string(4) "sell"
      }
      ["offert"]=>
      array(6) {
        ["param"]=>
        string(10) "445/443/TS"
        ["location"]=>
        array(1) {
          ["area"]=>
          array(5) {
            [0]=>
            string(2) "UK"
            [1]=>
            string(2) "ON"
            [2]=>
            string(3) "PIN"
            [3]=>
            string(2) "As"
            [4]=>
            string(3) "Ord"
          }
        }
        ["price"]=>
        string(10) "1059000.00"
        ["n_geo_y"]=>
        string(10) "51.1894791"
        ["n_geo_x"]=>
        string(10) "19.9072292"
        ["street"]=>
        string(7) "Example"
      }
    }
  }
}

Finally, I got array but not complete. For example, I can't find this part of XML code:

< param name="id">445/443/TS

As result, I can't find attributes name with value like id.

Is it possible to convert full XML code to array in PHP?


Solution

  • Is it possible to convert full XML code to array in PHP?

    Yes, it is, but mind that SimpleXMLElement already provides ArrayAccess so there is normally no need to do that as it has already been implemented. And for recursive traversal there is SimpleXMLIterator and RecursiveIteratorIterator.

    And for SimpleXML to JSON there is a three-part series on my blog.

    It is a bit dated but towards the end it is discussing writing custom replacers via JsonSerializable.

    As outlined, when we go more towards the leaf-nodes, SimpleXMLElement does some common tree normalization and therefore tends to shortcut attributes from time to time. Or even element names.

    The following is an example that merges attributes onto the object itself and puts the node-text on a property named as the element if necessary then. Only as a last resort creates tagged objects. That keeps the output dense:

    $sxml = new class ($xmlString) extends SimpleXMLElement implements JsonSerializable
    {
        public function jsonSerialize(): mixed
        {
            $name       = $this->getName();
            $attributes = iterator_to_array($this->attributes());
            $count      = $this->count();
            $named      = iterator_to_array($this);
            $names      = count($named);
            $list       = iterator_to_array($this, false);
    
            return match (true)
            {
                !$attributes => match (true)
                {
                    !$count           => (string)$this,
                    1 === $count,
                    $names === $count => $named,
                    1 === $names      => $list,
                    default           => array_map(fn($tag) => [$tag->getName() => $tag], $list)
                },
                !$count      => $attributes + [$name => (string)$this],
                1 === $count => $attributes + $named,
                default      => $this,
            };
        }
    };
    
    echo json_encode($sxml, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES), "\n";
    
    {
        "header": {
            "information": "Example information",
            "agency": "1@test.uk",
            "date": "2025-03-19 16:13:44",
            "version": "0.4",
            "path": "mail@mail.uk",
            "value": "full"
        },
        "offers_list": {
            "department": {
                "tab": "local",
                "type": "sell",
                "offert": {
                    "param": {
                        "name": "id",
                        "param": "445/443/TS"
                    },
                    "location": [
                        {
                            "level": "1",
                            "area": "UK"
                        },
                        {
                            "level": "2",
                            "area": "ON"
                        },
                        {
                            "level": "3",
                            "area": "PIN"
                        },
                        {
                            "level": "4",
                            "area": "As"
                        },
                        {
                            "level": "5",
                            "area": "Ord"
                        }
                    ],
                    "price": {
                        "currency": "USD",
                        "price": "1059000.00"
                    },
                    "n_geo_y": "51.1894791",
                    "n_geo_x": "19.9072292",
                    "street": "Example"
                }
            }
        }
    }