I recently had to tackle a bug in a legacy PHP application. This application receives a request from another application with JSON of the form:
{
"someList": [
"item A",
"item B"
],
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}
When this is deserialized to a native PHP "associative array", both the list and the map (with keys 0, 1, 2, and 3) look like lists. That's fine, I can work around that. However, this application does calculations on this data and adds some more to it before serializing back to JSON in roughly the same format and sends it along to another application. Here's where the problem is. Out of the box json_encode($data)
of the above results in:
{
"someList": [
"item A",
"item B"
],
"ratings": [
0.001234,
0.0666,
0.09876,
0.777777
]
}
My keys are all gone...
I see that I can use JSON_FORCE_OBJECT
a la echo json_encode($data, JSON_FORCE_OBJECT)
but then I get:
{
"someList": {
"0": "item A",
"1": "item B"
},
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}
Now I've got keys in the first list, which I don't want. Is there a way to serialize this JSON such that someList
will be a list (no keys) and ratings
will be a map/object (with keys 0, 1, 2, and 3)?
When calling json_encode on an array list, with numeric coherent indexes starting with 0, PHP will treat the list as an indexed array, and not an associative array. To force php to treat it as an associative array, you can cast the array to an object before calling json_encode.
Example:
$somelist = ["item A", "item B"];
$ratings = [0.001234, 0.666, 0.09876, 0.777777];
$final = ['someList' => $somelist, 'ratings' => (object) $ratings];
echo json_encode($final);
Output:
{["item A","item B"],{"0":0.001234,"1":0.666,"2":0.09876,"3":0.777777}}
I've also struggled with returning JSON with both regular arrays and objects with numeric keys in the same response.
A solution I found is that you can build up a stdObject and defining the keys using $obj->{'0'}
for example.
Here's a full example:
$decoded = json_decode('{
"someList": [
"item A",
"item B"
],
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}', true);
// Do stuff with $decoded['ratings']
$ratings = new \stdClass;
foreach ($decoded['ratings'] as $key => $rating) {
$ratings->{$key} = $rating;
}
echo json_encode([
'someList' => $decoded['someList'],
'ratings' => $ratings
]);
Which then will output the following:
{
"someList": [
"item A",
"item B"
],
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}
I wouldn't normally suggest "building" JSON manually, but concatenating bits of valid JSON should be fairly safe, and I think it's the only way you'll get this working.
$somelist = ["item A", "item B"];
$ratings = [0.001234, 0.666, 0.09876, 0.777777];
$json = sprintf(
'{"somelist":%s,"ratings":%s}',
json_encode($somelist),
json_encode($ratings, JSON_FORCE_OBJECT)
);
echo $json;
Or in case you have a larger object to work with, you can loop over the data to do this programmatically.
$original_json = '{"someList":["item A","item B"],"ratings""{"0":0.001234,"1":0.0666,"2":0.09876,"3":0.777777}}';
$data = json_decode($original_json);
// do whatever you need to do with the data
array_walk($data->someList, function(&$v, $k){$v .= " is changed";});
$vsprintf_args = [];
$format_str = "{";
foreach($data as $k=>$v) {
$format_str .= '%s:%s,';
$vsprintf_args[] = json_encode($k);
$vsprintf_args[] = json_encode($v, ($k === "ratings" ? JSON_FORCE_OBJECT : 0));
}
$format_str = trim($format_str, ",") . "}";
$json = vsprintf($format_str, $vsprintf_args);
echo $json;
Output:
{"somelist":["item A","item B"],"ratings":{"0":0.001234,"1":0.666,"2":0.09876,"3":0.777777}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With