In PHP it is easy to pass back an json objects by using the json_encode().
However is there an XML equivalent of this?
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' => 'encodingcharacters&test',
'me_n_you' => 'true'
);
echo (hhb_xml_encode ( $test ));
output:
foo
bar
foo
bar
baz
derp
derpmore
yes
no
lel
away
lala
quite
deep
indeed
checkmate
encoding<special>characters&test
true
function:
function hhb_xml_encode(array $arr, string $name_for_numeric_keys = 'val'): string {
if (empty ( $arr )) {
// avoid having a special case for and 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 ( '' . $xml . ' ' );
$ret = trim ( $domd->saveXML ( $domd->getElementsByTagName ( "root" )->item ( 0 ) ) );
assert ( 0 === strpos ( $ret, '' ) );
assert ( $endsWith ( $ret, ' ' ) );
$full = trim ( substr ( $ret, strlen ( '' ), - strlen ( ' ' ) ) );
$ret = '';
// ... seems each line except the first line starts with 2 ugly spaces,
// presumably its the 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 ();
$tmp = new DOMDocument();
@$tmp->loadXML ( '' . $tmp . ' ' );
foreach ( $tmp->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 );
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, '' ) );
assert ( $endsWith ( $ret, ' ' ) );
$ret = trim ( substr ( $ret, strlen ( '' ), - strlen ( ' ' ) ) );
// seems to be a bug with formatOutput on DOMDocuments that have used importNode with $deep=true..
$ret = $formatXML ( $ret );
return $ret;
}