user1933865
user1933865

Reputation:

php array to xml conversion even for nested data

I have an array which needs to be converted to xml format.

[name] => ABC
[email] => [email protected]
[phones] => Array
                (
                 [phone] => Array
                          (
                                [0] => Array
                                    (
                                        [mobile] => 91454599193
                                        [land] =>  9999999 
                                    )

                                [1] => Array
                                    (
                                        [mobile] => 54520199193
                                        [land] =>  9999999 
                                    )

                                [2] => Array
                                    (
                                        [mobile] => 90424249194
                                        [land] =>  5555555 
                                    )

                                [3] => Array
                                    (
                                        [mobile] => 44224199195
                                        [land] =>  8888888 
                                    )

                            )
)

I want this to be in the following format

<name>ABC</name>
<email>[email protected]</email>
 <phones>
 <phone>
   <mobile>545450199193</mobile>
   <land>9999999</land>
</phone>
 <phone>
   <mobile>575199193</mobile>
   <land>9999999</land>
</phone>
</phones>

Please help me out....

This is my function, I have written in this way.The problem I am facing is that the index which have numbers must display text in the XML node. Ex:- Below at the phone array, there are index [0], [1]. I want the [0], [1] to be replaced by <phone> in my XML page..

  function array_to_xml($value, &$xml_student_info) {
  foreach($value as $key => $value) {
    if(is_array($value)) {
        if(!is_numeric($key)){
            $subnode = $xml_student_info->addChild("$key");
            array_to_xml($value, $subnode);
        }
        else{
            array_to_xml($value, $xml_student_info);
         }
       }
        else {
            $xml_student_info->addChild("$key","$value");
        }
    }
}

Upvotes: 4

Views: 4272

Answers (2)

hakre
hakre

Reputation: 198204

You can solve your issue by using recursion. Recursion deals fine with nested data.

You do this by creating a single function that is adding the value to a parent XML element.

That function then call itself in case it finds nested data. It then will add the child element to the parent first and will the call itself with the new element added as parent.

If the data to be added is just a string, it will still create the child, but add the string value directly and leaves. That are the leaf-nodes, at most outside.

The add function then only needs to read the data you parse in and find out which of the four cases are to be dealt with:

  1. Just add a new element with a string value (string for a named element)
  2. Add multiple new elements with the same name (0-n indexed array for a named element)
  3. Add a new children and then add N new children in there (keyed array for a named element)
  4. Add N new children to the parent (keyed array for a numbered (not named) element)

For this answer I thought it would be nice to try this with an anonymous function. To get it recursive, it needs to reference itself. It then can be wrapped inside another function that will allow to create and importer for a SimpleXMLElement which allows the easy creation of XML. Wrapped once further would even allow to procude the XML as string, however I keep that to the external to make the usage clear:

require('inc/simplexml_pretty_print.php');

$xml      = new SimpleXMLElement('<root/>');
$importer = $createArrayImporter($xml);
echo simplexml_pretty_print($importer($array));

In this example $array is the array from your question. It contains those four cases, which are processed here in the $add function which will be returned wrapped inside another anonymous function when called $createArrayImporter() in the example above.

Before going in, let's review the input array:

$array = [
    'name'   => 'ABC',
    'email'  => '[email protected]',
    'phones' =>
    [
        'phone' =>
        [
            [
                'mobile' => '9000199193',
                'land'   => ' 9999999 ',
            ],
            [
                'mobile' => '9000199193',
                'land'   => ' 9999999 ',
            ],
            [
                'mobile' => '9000199194',
                'land'   => ' 5555555 ',
            ],
            [
                'mobile' => '9000199195',
                'land'   => ' 8888888 ',
            ],
        ],
    ],
];

And the output this creates:

<?xml version="1.0"?>
<root>
  <name>ABC</name>
  <email>[email protected]</email>
  <phones>
    <phone>
      <mobile>9000199193</mobile>
      <land> 9999999 </land>
    </phone>
    <phone>
      <mobile>9000199193</mobile>
      <land> 9999999 </land>
    </phone>
    <phone>
      <mobile>9000199194</mobile>
      <land> 5555555 </land>
    </phone>
    <phone>
      <mobile>9000199195</mobile>
      <land> 8888888 </land>
    </phone>
  </phones>
</root>

And how to the recursive function:

$add = function (SimpleXMLElement $subject, $key, $value) use (&$add)

The subject is the XML element to add data to, the key is the key in the array and the value likewise the value which may or may not to be nested.

So considering the most straight forward case, your keyed (all array indexes are strings) array should be added to the root element.

        case $isKeyed:
            foreach ($value as $oneof_key => $oneof_value) {
                $add($subject, $oneof_key, $oneof_value);
            }

This will pass the values along as nested. For example first on the next level is [name] => ABC, so a keyed string:

        case $isString && $hasKey:
            return $subject->addChild($key, $value);

This just adds the child element with the string value named as the key.

The next case is

[phones] => Array
     (
         [phone] => Array
             (
                 ...

That is a keyed array for a named element:

        case $isKeyed && $hasKey:
            $subject = $subject->addChild($key);
            // fall-through intended

It needs to add it's own key as child element comparable to the $isString && $hasKey case, but then needs to process it's array value further on. Which is also the nested case of a keyed array that has indexed children (0-N as indexes), so that more than one element with the same name can be added:

        case $isKeyed:
            foreach ($value as $oneof_key => $oneof_value) {
                $add($subject, $oneof_key, $oneof_value);
            }
            return true;

It will fall through that case and also create the fourth case for the keyed array that has no key but is just numbered.

That's also why I wrote that his is three and a half cases, the two last ones are sharing some parts between each other.

Wrapping this inside a closure makes this more accessible, which I will only outline in a little example here:

$createArrayImporter = function (SimpleXMLElement $subject) {

    $add = function (SimpleXMLElement $subject, $key, $value) use (&$add) {

         ...

    };

    return function (Array $array) use ($subject, $add) {

        $add($subject, null, $array);
        return $subject;
    };
};

So this can kick things off. You can read the full code in a Gist of mine.

You find a similar example but m ore object oriented in the following answer, which also solves it via recursion:

The format of the array - even if it may look similar - is actually different to yours.

And for more pointers there is:

Which deals with different types of arrays to XML conversion.

Upvotes: 3

devOp
devOp

Reputation: 3180

The Problem is that you can not have same keys in one array or Object. In case of simplexml there's a object where the xml is saved. The solution is build up the xml by yourself.

Upvotes: 0

Related Questions