Array2XML: convert PHP Array to XML (with attributes and CDATA)

Array2XML is a class to convert an array in PHP to XML. It allows you to parse a multidimensional array into XML including attributes unlike other scripts available on the internet. It returns the XML in form of DOMDocument object for further manipulation.

This library is very helpful when you know the scheme before hand and you have to generate an XML for it using dynamic values from the database.

The resulting XML can be converted back to an Array using the XML2Array class.

Usage

The usage is pretty simple. You have to include the class file in your code and call the following function.

$xml = Array2XML::createXML('root_node_name', $php_array);
echo $xml->saveXML();

Important thing to note is that the $xml object returned is of type DOMDocument and hence you can perform further operations on it.

Optionally you can also set the version of XML and encoding by calling the Array2XML::init() function before calling the Array2XML::createXML() function.

Array2XML::init($version /* ='1.0' */, $encoding /* ='UTF-8' */);

It throws exception if the tag name or attribute name has illegal chars as per W3C spec.

Array Structure conventions

The array passed to the Array2XML::createXML() function follows few conventions, which are quite literal and easy to learn/use. The examples below demonstrate their usage

  1. Empty Nodes: Following will create an empty node.
    $books = array();  // or
    $books = null;  // or
    $books = '';
    $xml = Array2XML::createXML('books', $books);
     
    // all three cases above create <books/>
  2. Attributes: Attributes can be added to any node by having a @attributes key in the array
    $books = array(
        '@attributes' => array(
            'type' => 'fiction',
            'year' => 2011,
            'bestsellers' => true
        )
    );
    $xml = Array2XML::createXML('books', $books);
     
    // creates <books type="fiction" year="2011" bestsellers="true"/>
  3. Node Value: For nodes without attributes, value can be assigned directly, else we need to have a @value key in the array. Following examples will make it clear
    $books = 1984;  // or
    $books = array(
        '@value' = 1984
    );
    // creates <books>1984</books>
     
    $books = array(
        '@attributes' => array(
            'type' => 'fiction'
        ),
        '@value' = 1984
    );
    // creates <books type="fiction">1984</books>
     
    $books = array(
        '@attributes' => array(
            'type' => 'fiction'
        ),
        'book' => 1984
    );
    /* creates 
    <books type="fiction">
      <book>1984</book>
    </books>
    */
     
    $books = array(
        '@attributes' => array(
            'type' => 'fiction'
        ),
        'book'=> array('1984','Foundation','Stranger in a Strange Land')
    );
    /* creates 
    <books type="fiction">
      <book>1984</book>
      <book>Foundation</book>
      <book>Stranger in a Strange Land</book>
    </books>
    */
  4. Complex XML: Following example clarifies most of the usage of the library
    $books = array(
        '@attributes' => array(
            'type' => 'fiction'
        ),
        'book' => array(
            array(
                '@attributes' => array(
                    'author' => 'George Orwell'
                ),
                'title' => '1984'
            ),
            array(
                '@attributes' => array(
                    'author' => 'Isaac Asimov'
                ),
                'title' => array('@cdata'=>'Foundation'),
                'price' => '$15.61'
            ),
            array(
                '@attributes' => array(
                    'author' => 'Robert A Heinlein'
                ),
                'title' =>  array('@cdata'=>'Stranger in a Strange Land'),
                'price' => array(
                    '@attributes' => array(
                        'discount' => '10%'
                    ),
                    '@value' => '$18.00'
                )
            )
        )
    );
    /* creates 
    <books type="fiction">
      <book author="George Orwell">
        <title>1984</title>
      </book>
      <book author="Isaac Asimov">
        <title><![CDATA[Foundation]]></title>
        <price>$15.61</price>
      </book>
      <book author="Robert A Heinlein">
        <title><![CDATA[Stranger in a Strange Land]]</title>
        <price discount="10%">$18.00</price>
      </book>
    </books>
    */

Detailed Example

You can see a much more detailed example here.

Download (v0.8, 2 May, 2012)

The code is released under Apache License 2.0

Plaint text PHP source | Formatted PHP source

• • •

55 Responses to Array2XML: convert PHP Array to XML (with attributes and CDATA)

  1. Chris Neal says:

    This works perfectly. Good job!

  2. Tim Lee says:

    Do you know of any good class to do the reverse? Convert xml into an array?

    Thanks,

  3. Lion says:

    Thanks,

  4. Pete says:

    I tried 1/2 dozen of these and this is the only one that actually worked!

    Great job!

    Thanks for sharing!

  5. Liel Dulev says:

    Hi, Great Class, but I found a bug:

    You should always use

    htmlspecialchars($value,ENT_QUOTES,'UTF-8');

    and not

    htmlentities($value);

    in XML, as htmlentities replaces chars that should not be replaced and it breaks the xml.

    • Ben says:

      Using PHP version

      I ran into a problem of double encoding with your code. You are encoding the xml string into things like DOMText nodes and DOMAttr values. When you call saveXML() on the DOMDocument, it encodes your already encoded string resulting in things like &amp;. This isn’t what should be expected.

      Just wanted to let you know. :)

  6. Liel Dulev says:

    Another Bug:

    on line 108 it should be

    if(!empty($arr) || $arr===false) { ... }

    otherwise you will get empty nodes and not nodes with the value of falseā€¦

  7. chrisA says:

    Hi, thanks for the class. I want to use your xml class for generating of xml document to use with a Rest service. The root name has a URL attached. something like this:

    how can i have something like above as/in the root name, not just

  8. ChrisA says:

    I have seen the solution in the advance examples, the resturant example answered by question. Thanks

  9. ChrisA says:

    I am having error with this class in that i can not use ‘v’ as an attribute?
    I want to use a web service api that required that i send a “v” but array2xml doesn’t let me do that it will accept everything but not ‘v’ as an attribute “v” meaning version number.

    how can i solve this problem?

    • Lalit says:

      Fixed the regex which didn’t allow tag names which are single character in length. This should work now.
      Thanks for pointing it out. Please take the new version (0.3) of the file.

  10. Mario says:

    Has anyone found a solution for using CDATA?

  11. Anny says:

    Hi Lalit, I needed to use your class in a Magento project so adapted it to fit while also restructuring bits of code to make it more human-readable and I think a little easier to use as a developer. You can find the source code here, https://github.com/annyfm/Anny_Array2xml/blob/master/app/code/community/Anny/Array2xml/Model/Converter.php and I’ve preserved your license information and version notes with a link back to this page. Hope you find it interesting!

  12. Pingback: Ligio Blog » Array to XML in PHP

  13. Eddie says:

    Just what I was looking for, searched for ages to find a nice simple solution like this!

    Many thanks :)

  14. jagan says:

    Hi Lalit,

    I am trying to use your class but I have arrays for [@value], and it is failing how do I use if the [@value] is another array?

    Please let me know.

    Thanks,

    • Lalit says:

      Hi Jagan,
      If you need value to be an array you just need to directly name the attribute with the node name, no need to use @value.
      Example:

      $xml['node_name'][] = 'value1'; 
      $xml['node_name'][] = 'value2';
  15. m0rfeusl says:

    Great thanks for this perfect solution! You are master ;)

  16. majki says:

    You saved my ass!!! You are my hero

  17. Tim Lee says:

    Thank you very much for this class, it has helped me greatly. I did have one issue with the cdata processing though (it doesn’t seem to process cdata when it’s in a node that has the same name as the surrounding nodes), would you be able to assist me with this?

    The array I give it is this:

    $input = array(
                "show" => array(
                    array(
                        "dog" => "Brian",
                        "kid" => array(
                            "Chris",
                            "Meg",
                            "@cdata" => "<em>Stewie</em>"
                        ),
                        "@attributes" => array("name" => "Family Guy")
                    ),
                    array(
                        "empty" => array(),
                        "foo" => array(
                            "@attributes" => array(
                                "empty" => ""
                            )
                        ),
                        "zero" => "0",
                        "@attributes" => array(
                            "name" => "Edge Cases",
                            "zero" => "0",
                            "empty" => ""
                        )
                    )
                ),
            );

    With a node name of “tv”, I get the following XML:

    ...
        <dog>Brian</dog>
        <kid>Chris
        <kid>Meg
        <kid>&amp;lt;em&amp;gt;Stewie&amp;lt;/em&amp;gt;</kid>
    ...

    Is this format of cdata something that could be accommodated?

    Thanks,
    Tim Lee

    • Lalit says:

      Thanks for identifying this edge case
      Immediate fix would be instead of using:

      "show" => array(
          array(
              "dog" => "Brian",
              "kid" => array(
                  "Chris",
                  "Meg",
                  array(
                      "@cdata" => "<em>Stewie</em>"
                  )
              ),
              "@attributes" => array("name" => "Family Guy")
          )
      )
  18. What such a nice class, thanks for your works.

  19. vernes says:

    In your example you have multiple children with thesame name: array( 'book' => array('1984','Foundation','etc') )

    These titles do not have an associated key assigned to them.
    But when you need to access them, they do.
    Your class does not accept array’s where these numerical key’s are defined explicitly.

    Just thought I mention this.

  20. Ario says:

    Found an issue with version 0.6, on line 130 of the code, add this:

    } else {
    $node->appendChild(self::convert($key, $value));

  21. Ario says:

    This code of yours is the most complete solution I found on the interwebs. So great work :)

    I just did a little tweak to your code though, in method Array2XML::createXML, I replaced this line:


    $xml->appendChild(self::convert($node_name, $arr));

    to this.


    if (count($arr) > 1) {
    $xml->appendChild(self::convert($node_name, $arr));
    } else {
    $root_element = array_pop(array_keys($arr));
    $xml->insertBefore(self::convert($root_element, $arr[$root_element]));
    }

    Why? Because sometimes I want to use the first array key found to be the root element. Instead of having to specify a new root element as the container.

  22. Ben says:

    Possible bug?

    When converting Arrays where the index is a numeric version, eg:

    [similarArtists] => Array
            (
                [0] => Array
                    (
                        [name] => Freddie Mercury
                        [match] => 1
                    )
     
                [1] => Array
                    (
                        [name] => Brian May
                        [match] => 0.656711
                    )

    Converts into XML as…

    <similarArtists>
    <name>Freddie Mercury</name>
    <match>1</match>
    </similarArtists>
    <similarArtists>
    <name>Brian May</name>
    <match>0.656711</match>
    </similarArtists>

    Where ‘similarArtists’ should be one container node with 0,1,2 etc sub nodes.

    Any thoughts?

  23. vladimir onisim says:

    Hello,

    I think there is a small issue still. The & is xml escaped twice.
    The problem comes from the fact that you don’t need to xml escape your data yourself with neither htmlspecialchars nor htmlentities because the DomDocument takes care of this protection. I removed those instructions and it works.
    Of course, you can’t simply display this output XML in a web page, you must do a htmlentities( $xml->saveXML() ) to see the final string.

    The only thing I don’t like with the DomDocument class, is that it doesn’t xml escape the simple quote (‘) as it should (it works for & ” but not ‘ ). They consider they will never internally use a (‘) as reserved xml char (but use ” instead) so they don’t need to encode the (‘). It’s a small mistake in my humble opinion.

  24. Tom Conlon says:

    Hi Lalit,

    Many thanks for this, very nice code!

    One problem I’ve found is if getting data from a query using Zend_DB
    (default fetchmode is FETCH_ASSOC)
    $stmt = $this->_db->query($sql);
    $rows = $stmt->fetchAll();

    It produces an array like this:
    Array (
    [0] => Array ( [ID] => 11002 [NAME] => S & A Produce (UK) Ltd )
    [1] => Array ( [ID] => 11003 [NAME] => S C Motor Factors )
    …. )

    which chokes immediately with an Exception
    ([Array2XML] Illegal character in tag name….) because the outmost array is unnamed.

    Am I missing something or is there anything that can be done here?

    Many thanks,
    Tom

    • Lalit says:

      The problem is the numeric key 0, 1.
      Since XML node names cannot start with a number it gives an error.

      You can use something like this:

      $stmt = $this->_db->query($sql);
      $rows = array('row' => $stmt->fetchAll());
      Array2XML::createXML('root_node_name',$rows);

      This will output

      <root_node_name>
      <row>...</row>
      <row>...</row>
      </root_node_name>
  25. Sandro says:

    Searching for a class like this for hours, thanks man for this. Saved my life. Don’t need to hack somesthing like this by myself anymore.

  26. AAnthony says:

    sorry I’m french

    If I use $array = XML2Array::createArray($xml);
    and after $xml = Array2XML::createXML('root_node_name', $array);

    in the save file we add NODE
    so now new_xml in not the same as old_xml !!!

  27. David Pilz says:

    Very nice solution, thanks a lot!

  28. pruchai says:

    This is nice, but unfortunately it fails to deal with mixed arrays and objects.
    Sigh…. i guess i have to write my own :(

  29. Bill says:

    Hey! Just wanted to say thanks for an awesome class :) Worked really well! Kick ass!

  30. Rod Elias says:

    Hello mate!
    This class is life saver for sure.
    Such an amazing work you’ve done. Congrats!

  31. kor3k says:

    yeah i got the same result. it seems that every node HAS to be an array, not scalar

    ‘title’ => ’1984′ // this does not work, it is not presented in the output

    ‘title’ => array( ’1984′ ) //this works

    but this behaviour is very very bad. in fact, you have to recursively walk through the whole array and convert every non-array value into array with one entry, this should be done automatcially

  32. Lalit says:

    kor3k,
    I have fixed the issue. This bug was introduced in v0.7

  33. edward.CC says:

    please change ‘ if($k==”@attributes”) ‘ for ‘ if($k===”@attributes”) ‘, cuz can make errors.