xml - Is there an xml_encode() like json_encode() in PHP?

473

In PHP, it is easy to pass back JSON objects by using thejson_encode() function.

Is there an XML equivalent for this?

844

Answer

Solution:

You can define your ownxml_encode() function such as this the one from http://darklaunch.com/2009/05/23/php-xml-encode-using-domdocument-convert-array-to-xml-json-encode

function xml_encode($mixed, $domElement=null, $DOMDocument=null) {
    if (is_null($DOMDocument)) {
        $DOMDocument =new DOMDocument;
        $DOMDocument->formatOutput = true;
        xml_encode($mixed, $DOMDocument, $DOMDocument);
        echo $DOMDocument->saveXML();
    }
    else {
        // To cope with embedded objects 
        if (is_object($mixed)) {
          $mixed = get_object_vars($mixed);
        }
        if (is_array($mixed)) {
            foreach ($mixed as $index => $mixedElement) {
                if (is_int($index)) {
                    if ($index === 0) {
                        $node = $domElement;
                    }
                    else {
                        $node = $DOMDocument->createElement($domElement->tagName);
                        $domElement->parentNode->appendChild($node);
                    }
                }
                else {
                    $plural = $DOMDocument->createElement($index);
                    $domElement->appendChild($plural);
                    $node = $plural;
                    if (!(rtrim($index, 's') === $index)) {
                        $singular = $DOMDocument->createElement(rtrim($index, 's'));
                        $plural->appendChild($singular);
                        $node = $singular;
                    }
                }

                xml_encode($mixedElement, $node, $DOMDocument);
            }
        }
        else {
            $mixed = is_bool($mixed) ? ($mixed ? 'true' : 'false') : $mixed;
            $domElement->appendChild($DOMDocument->createTextNode($mixed));
        }
    }
}
695

Answer

Solution:

JSON can express php arrays, integers, strings, etc. natively. XML has no such concepts - just elements, attributes, and text. If you want to transfer an object verbatim, use JSON. If you want to implement a complex API, use XML, for example the php DOM interface.

458

Answer

Solution:

You could usexmlrpc_encode.

 xmlrpc_encode ($your_array);

Be careful because this function is EXPERIMENTAL.

Reference: http://php.net/manual/en/function.xmlrpc-encode.php

360

Answer

Solution:

here is one for php7.0+ , i bet it is far from optimal , the code is non-trivial, and it has NOT been tested much, but at least it works for my data (unlike Seph's code)...

example:

$test = array (
        'normal1' => 'foo',
        'normal2' => 'bar',
        'foo_assoc' => [ 
                'foo',
                'bar',
                'baz',
                [ 
                        'derp',
                        'derpmore' 
                ] 
        ],
        'foo_nonassoc' => [ 
                'derppp' => 'yes',
                'daarpp' => 'no',
                'lel',
                'far' => 'away' 
        ],
        'normal3' => 'lala',
        'deep' => [ 
                'deeper' => [ 
                        'deeper2' => [ 
                                'deepest' => [ 
                                        'quite',
                                        'deep',
                                        'indeed' 
                                ],
                                'checkmate' 
                        ] 
                ] 
        ],
        'special' => 'encoding<special>characters&test',
        'me_n_you' => 'true' 
);

echo (hhb_xml_encode ( $test ));

output:

<normal1>foo</normal1>
<normal2>bar</normal2>
<foo_assoc>foo</foo_assoc>
<foo_assoc>bar</foo_assoc>
<foo_assoc>baz</foo_assoc>
<foo_assoc>derp</foo_assoc>
<foo_assoc>derpmore</foo_assoc>
<foo_nonassoc>
  <derppp>yes</derppp>
  <daarpp>no</daarpp>
  <foo_nonassoc>lel</foo_nonassoc>
  <far>away</far>
</foo_nonassoc>
<normal3>lala</normal3>
<deep>
  <deeper>
    <deeper2>
      <deepest>quite</deepest>
      <deepest>deep</deepest>
      <deepest>indeed</deepest>
      <deeper2>checkmate</deeper2>
    </deeper2>
  </deeper>
</deep>
<special>encoding&lt;special&gt;characters&amp;test</special>
<me_n_you>true</me_n_you>

function:

  • Edit: fixed a bug with encoding empty arrays.
  • Edit: made the code PHP8-compatible
    function hhb_xml_encode(array $arr, string $name_for_numeric_keys = 'val'): string {
        if (empty ( $arr )) {
            // avoid having a special case for <root/> and <root></root> i guess
            return '';
        }
        $is_iterable_compat = function ($v): bool {
            // php 7.0 compat for php7.1+'s is_itrable
            return is_array ( $v ) || ($v instanceof \Traversable);
        };
        $isAssoc = function (array $arr): bool {
            // thanks to Mark Amery for this
            if (array () === $arr)
                return false;
            return array_keys ( $arr ) !== range ( 0, count ( $arr ) - 1 );
        };
        $endsWith = function (string $haystack, string $needle): bool {
            // thanks to MrHus
            $length = strlen ( $needle );
            if ($length == 0) {
                return true;
            }
            return (substr ( $haystack, - $length ) === $needle);
        };
        $formatXML = function (string $xml) use ($endsWith): string {
            // there seems to be a bug with formatOutput on DOMDocuments that have used importNode with $deep=true
            // on PHP 7.0.15...
            $domd = new DOMDocument ( '1.0', 'UTF-8' );
            $domd->preserveWhiteSpace = false;
            $domd->formatOutput = true;
            $domd->loadXML ( '<root>' . $xml . '</root>' );
            $ret = trim ( $domd->saveXML ( $domd->getElementsByTagName ( "root" )->item ( 0 ) ) );
            assert ( 0 === strpos ( $ret, '<root>' ) );
            assert ( $endsWith ( $ret, '</root>' ) );
            $full = trim ( substr ( $ret, strlen ( '<root>' ), - strlen ( '</root>' ) ) );
            $ret = '';
            // ... seems each line except the first line starts with 2 ugly spaces,
            // presumably its the <root> element that starts with no spaces at all.
            foreach ( explode ( "\n", $full ) as $line ) {
                if (substr ( $line, 0, 2 ) === '  ') {
                    $ret .= substr ( $line, 2 ) . "\n";
                } else {
                    $ret .= $line . "\n";
                }
            }
            $ret = trim ( $ret );
            return $ret;
        };
        
        // $arr = new RecursiveArrayIterator ( $arr );
        // $iterator = new RecursiveIteratorIterator ( $arr, RecursiveIteratorIterator::SELF_FIRST );
        $iterator = $arr;
        $domd = new DOMDocument ();
        $root = $domd->createElement ( 'root' );
        foreach ( $iterator as $key => $val ) {
            // var_dump ( $key, $val );
            $ele = $domd->createElement ( is_int ( $key ) ? $name_for_numeric_keys : $key );
            if (! empty ( $val ) || $val === '0') {
                if ($is_iterable_compat ( $val )) {
                    $asoc = $isAssoc ( $val );
                    $tmp = hhb_xml_encode ( $val, is_int ( $key ) ? $name_for_numeric_keys : $key );
                    // var_dump ( $tmp );
                    // die ();
                    $tmpDom = new DOMDocument();
                    @$tmpDom->loadXML ( '<root>' . $tmp . '</root>' );
                    foreach ( $tmpDom->getElementsByTagName ( "root" )->item ( 0 )->childNodes ?? [ ] as $tmp2 ) {
                        $tmp3 = $domd->importNode ( $tmp2, true );
                        if ($asoc) {
                            $ele->appendChild ( $tmp3 );
                        } else {
                            $root->appendChild ( $tmp3 );
                        }
                    }
                    unset ( $tmp, $tmp2, $tmp3, $tmpDom );
                    if (! $asoc) {
                        // echo 'REMOVING';die();
                        // $ele->parentNode->removeChild($ele);
                        continue;
                    }
                } else {
                    $ele->textContent = $val;
                }
            }
            $root->appendChild ( $ele );
        }
        $domd->preserveWhiteSpace = false;
        $domd->formatOutput = true;
        $ret = trim ( $domd->saveXML ( $root ) );
        assert ( 0 === strpos ( $ret, '<root>' ) );
        assert ( $endsWith ( $ret, '</root>' ) );
        $ret = trim ( substr ( $ret, strlen ( '<root>' ), - strlen ( '</root>' ) ) );
        // seems to be a bug with formatOutput on DOMDocuments that have used importNode with $deep=true..
        $ret = $formatXML ( $ret );
        return $ret;
    }
459

Answer

Solution:

My contrib:

function xml_encode(mixed $value=null, string $key="root", SimpleXMLElement $parent=null){
    if(is_object($value)) $value = (array) $value;
    if(!is_array($value)){
        if($parent === null){   
            if(is_numeric($key)) $key = 'item';             
            if($value===null) $node = new SimpleXMLElement("<$key />");
            else              $node = new SimpleXMLElement("<$key>$value</$key>");
        }
        else{
            $parent->addChild($key, $value);
            $node = $parent;
        }
    }
    else{
        $array_numeric = false;
        if($parent === null){ 
            if(empty($value)) $node = new SimpleXMLElement("<$key />");
            else              $node = new SimpleXMLElement("<$key></$key>");
        }
        else{
            if(!isset($value[0])) $node = $parent->addChild($key);
            else{
                $array_numeric = true;
                $node = $parent;
            }
        }
        foreach( $value as $k => $v ) {
            if($array_numeric) xml_encode($v, $key, $node);
            else xml_encode($v, $k, $node);
        }
    }       
    return $node;
}

Simple Example:

   $a = "hello";
   $xml_element = xml_encode($a,'a');
   echo $xml_element->asXML();

Null Example:

   $xml_element = xml_encode(null,'example');
   echo $xml_element->asXML();

Complex Example:

   $w = new stdClass();
   $w->special = true;
   $w->name = 'Birthday Susan';
    
   $v = new stdClass();
   $v->name = 'John';
   $v->surname = 'Smith';
   $v->hobbies = array('soccer','cinema');
   $v->job = 'policeman';
   $v->events = new stdClass();
   $v->events->tomorrow = false;
   $v->events->yesterday = true;
   $v->events->list = array($v->hobbies, $w);
    
   $xml_element = xml_encode($v,'oembed');
   echo $xml_element->asXML();
44

Answer

Solution:

This works for me in most cases:

$str = htmlentities($str , ENT_XML1);

Docs: http://php.net/manual/en/function.htmlentities.php

People are also looking for solutions to the problem: php - Symfony: uploading a file without sfWidgetFormInputFile()

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.