0

I have a function who convert an array to XML but it doesn't work in any case.

public function array_to_xml($data, &$xml_data) {
    foreach($data as $key => $value) {
        if (is_numeric($key)) {
            $key = 'item'.$key;
        }
        if (is_array($value)) {
            $subnode = $xml_data->addChild($key);
            $this->array_to_xml($value, $subnode);
        } else {
            $xml_data->addChild("$key",htmlspecialchars("$value"));
        }
     }
}

But if I send this array it will returns <item0...n> :

$xml = new \SimpleXMLElement('<?xml version="1.0"?><offices></offices>');

$first = array("id" => 1, "name" => "firstOffice");
$second = array("id" => 2, "name" => "secondOffice");
$offices = array("office" => array($first, $second));

return array_to_xml($data, $xml);

OUTPUT

<offices>
    <office>
        <item0>
            <id>1</id>
            <name>firstOffice</name>
        </item0>
        <item1>
            <id>2</id>
            <name>secondOffice</name>
        </item1>
    </office>
</offices>

How can I edit my function to get this result :

<offices>
    <office>
        <id>1</id>
        <name>firstOffice</name>
    </office>
    <office>
        <id>2</id>
        <name>secondOffice</name>
    </office>
</offices>

Actually I'll return JSON or XML based on the accept header then I have to use the same array for the JSON and the XML.

Here is a part of my current code :

public function getOffices() {
    $tools = new Tools();
    $accept = $this->request->getHeader('accept');
    $xml = new \SimpleXMLElement('<?xml version="1.0"?><offices></offices>');

    $offices = Offices::find();  // Find all offices, I'm using Phalcon PHP ORM

    return $tools->formatAdapter($accept, array("office" => $offices->toArray()), 200, $xml);
}

Tools functions class

public function array_to_xml($data, &$xml_data) {
    foreach($data as $key => $value) {
        if (is_numeric($key)) {
            $key = 'item'.$key;
        }
        if (is_array($value)) {
            $subnode = $xml_data->addChild($key);
            $this->array_to_xml($value, $subnode);
        } else {
            $xml_data->addChild("$key",htmlspecialchars("$value"));
        }
     }
}

public function formatAdapter($type, $data, $code, &$xml_data) {
    if ($type == 'application/json') {
        return $this->JSONBuilder($data, $code);
    } else {
        $this->array_to_xml($data, $xml_data);
        return $this->XMLBuilder($xml_data->asXML(), $code);
    }
}

public function JSONBuilder($data, $code) {
    $response = new Response();

    $message = $this->getResponseDescription($code);

    $response->setHeader('Content-Type', 'application/json');
    $response->setStatusCode($code, $message);
    $response->setJsonContent($data);

    return $response;
}

function XMLBuilder($xml, $code) {
    $response = new Response();

    $message = $this->getResponseDescription($code);

    $response->setHeader('Content-Type', 'application/xml');
    $response->setStatusCode($code, $message);
    $response->setContent($xml);

    return $response;
}

Edit formatAdapter function

public function formatAdapter($type, $data, $code, &$xml_data, $loop = false) {
    if ($type == 'application/json') {
        return $this->JSONBuilder($data, $code);
    } else {
        if ($loop) {
            foreach ($data as $key => $value) {
                foreach ($value as $obj) {
                    $this->array_to_xml(array($key => $obj), $xml_data);
                }
            }
        } else {
            $this->array_to_xml($data, $xml_data);
        }
        return $this->XMLBuilder($xml_data->asXML(), $code);
    }
}

It works if I add this loop inside my function and $loop parameter. But as you can see I have 2 foreach loops. It's possible to avoid the first foreach loop ? Because I'll loop once everytime. I tried with array_value() but there it doesn't work.

EDIT FIX

Finally I found a solution for my formatAdapter function, but it will works just for the first object. If someone has a better solution :

public function formatAdapter($type, $data, $code, &$xml_data, $loop = false) {
    if ($type == 'application/json') {
        return $this->JSONBuilder($data, $code);
    } else {
        if ($loop) {
            $key = key($data);
            foreach ($data[$key] as $obj) {
                $this->array_to_xml(array($key => $obj), $xml_data);
            }
        } else {
            $this->array_to_xml($data, $xml_data);
        }
        return $this->XMLBuilder($xml_data->asXML(), $code);
    }
}

Thanks to @iainn

4
  • not sure I understand the question. instead of the first output you are interested to get the second output? Commented Jun 14, 2017 at 8:52
  • Yes. item0, item1 is not explicit. I need to specify the object name. Here I have an array of offices then I want to write <office> for each offices Commented Jun 14, 2017 at 8:55
  • but you are running 'foreach' $key = 'item'.$key; ...? Commented Jun 14, 2017 at 8:59
  • Yep I have to replace this line by the previous key but I don't know how Commented Jun 14, 2017 at 8:59

1 Answer 1

1

If you don't want to modify the existing function, then you can use the following:

$xml = new \SimpleXMLElement('<?xml version="1.0"?><offices></offices>');

$first = array("id" => 1, "name" => "firstOffice");
$second = array("id" => 2, "name" => "secondOffice");

array_to_xml(array("office" => $first), $xml);
array_to_xml(array("office" => $second), $xml);

echo $xml->asXML();

The function assumes that if you only pass in a single array, you want them grouped into itemX tags. If you want separate office tags, you need to call the function more than once with each office.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer. I don't want to loop and call array_to_xml because I made a function who returns XML or JSON then I have to send just one array. Then JSON and XML use the same array. Maybe I have to create another function when I'm in this case ?
Maybe I can add a foreach loop inside my formatAdapter function for the XML and a parameter $loop true / false
I adapted your solution in my function and it works for JSON and XML. Thanks. But it will not works if I have to loop on another subnode

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.