PHP/json_encode: dealing with mixed arrays and objects with numeric properties - php

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}}

Was able to find a solution by using stdClass instead of an associative array when decoding the original JSON via json_decode($json, false);. Then when json_encodeing the resulting stdClass the keys will be preserved.
Here's a full example:
<?php
$json = <<<JSON
{
"someList": [
"item A",
"item B"
],
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}
JSON;
// Passing false for the second param (or omitting it)
// returns a stdClass instead of associative array
$data = json_decode($json, false);
echo json_encode($data, JSON_PRETTY_PRINT);
Which outputs:
{
"someList": [
"item A",
"item B"
],
"ratings": {
"0": 0.001234,
"1": 0.0666,
"2": 0.09876,
"3": 0.777777
}
}

Related

Append data to top of the JSON file using PHP form

I wanted to append a php from data in JSON file to top.
I used some PHP functions like -
array_unshift($array_data, $extra);
$array_data = array_reverse($array_data);
Example -
<?php
$message = '';
$error = '';
if(isset($_POST["submit"]))
{
if(empty($_POST["name"]))
{
$error = "<label class='text-danger'>Enter Name</label>";
}
else if(empty($_POST["author"]))
{
$error = "<label class='text-danger'>Enter Author</label>";
}
else if(empty($_POST["category"]))
{
$error = "<label class='text-danger'>Enter Thumbnail</label>";
}
else if(empty($_POST["url"]))
{
$error = "<label class='text-danger'>Enter URL</label>";
}
else
{
if(file_exists('wallpaper.json'))
{
$current_data = file_get_contents('wallpaper.json');
$array_data = json_decode($current_data, true);
$extra = array(
'name' => $_POST['name'],
'author' => $_POST["author"],
'category' => $_POST["category"],
'url' => $_POST["url"]
);
$array_data[] = $extra;
//array_unshift($array_data, $extra); // used this as well
$array_data = array_reverse($array_data);
$final_data = json_encode($array_data);
if(file_put_contents('wallpaper.json', $final_data))
{
$message = "<label class='text-success'>File Appended Success fully</p>";
}
}
else
{
$error = 'JSON File not exits';
}
}
}
?>
Both of these PHP functions, they work perfectly until you add data 2 times but once you add 3rd data or more then it looks like this -
[
{
"name": "3",
"author": "3",
"category": "3",
"url": "3"
},
{
"name": "1",
"author": "1",
"category": "1",
"url": "1"
},
{
"name": "2",
"author": "2",
"category": "2",
"url": "2"
}
]
But it should look like this -
[
{
"name": "3",
"author": "3",
"category": "3",
"url": "3"
},
{
"name": "2",
"author": "2",
"category": "2",
"url": "2"
},
{
"name": "1",
"author": "1",
"category": "1",
"url": "1"
}
]
At beggining you have [1] then you insert [2] and after $array_data[] = $extra; goes to [1][2] and the you reverse array [2][1]. At this moment when you insert a new value you have [2][1][3] and the after reversing [3][1][2] the solution would be reversing before insert extra:
test.php:
<?php
if(file_exists('wallpaper.json'))
{
$current_data = file_get_contents('wallpaper.json');
$array_data = json_decode($current_data, true);
$extra = array(
'name' => $_REQUEST['name'],
'author' => $_REQUEST["author"],
'category'=> $_REQUEST["category"],
'url' => $_REQUEST["url"]
);
echo $extra['name'];
$array_data = array_reverse($array_data);
$array_data[] = $extra;
$array_data = array_reverse($array_data);
$final_data = json_encode($array_data);
if(file_put_contents('wallpaper.json', $final_data))
{
$message = "<label class='text-success'>File Appended Success fully</p>";
}
}
?>
to run I used to pass parameters:
http://localhost/test.php?name=4&author=4&category=4&url=4
$posted[] = $_POST;
$current_data = file_get_contents('wallpaper.json');
$decoded = json_decode($current_data,true);
//You merge together posted array values with the current array
//that is in the current file (after decoding the json)
//$posted is first part of the array because you start to merge from that
//array
$new_arr = array_merge($posted, $decoded);
//Encode the new array into JSON and save it
$encoded_json = json_encode($new_arr,);
file_put_contents('wallpaper.json', $encoded_json);
First, don't push the $_POST data into its own subarray. It is already in the correct structure to call array_unshift() directly on it. In other words, you don't need to index it -- unshift will apply the 0 key for you.
Code: (Demo)
$fileContents = '[{"name":"B1","author":"B2","category":"B3","url":"B4"},{"name":"A1","author":"A2","category":"A3","url":"A4"}]';
$array = json_decode($fileContents, true);
$_POST = [
'name' => 'C1',
'author' => 'C2',
'category' => 'C3',
'url' => 'C4',
];
array_unshift($array, $_POST);
echo json_encode($array);
Output:
[{"name":"C1","author":"C2","category":"C3","url":"C4"},{"name":"B1","author":"B2","category":"B3","url":"B4"},{"name":"A1","author":"A2","category":"A3","url":"A4"}]
Secondly, I don't support the notion of building this json file if there is any way that you can avoid it. I mean, as you your file size increases, your server will have to work harder and harder and harder to parse the json, manipulate it, re-encode it every time you want to make a change.
I might recommend that you restructure your file to be a txt file which is a collection of json strings -- all of which are separately written on each line. This way you don't have to unpack and repack your data every time, you just prepend the new json string to the file and walk away. *Caveat, this will fail if the data that you are storing contains newline characters -- I don't know if your project might receive such data.
p.s. If you are not a ultra-purist developer and your incoming data is as tame as it looks from your post, you can hack at the json string and lighten the workload like this:
Code: (Demo)
$newJson = json_encode($_POST);
if ($fileContents) {
$fileContents = substr_replace($fileContents, $newJson . ',', 1, 0);
} else {
$fileContent = '[' . $newJson . ']';
}
echo $fileContents;

Remove Index inside variable

I have code like this, but how can I remove "1","2","3" inside "collaterals" ?
{
"brokerPartner": "XX",
"collaterals": {
"1": {
"stockCode": "ABC",
"contractCode": "GE01905438831212",
"qtyEfek": "900"
},
"2": {
"stockCode": "DEF",
"contractCode": "GE01905438831212",
"qtyEfek": "1900"
},
"3": {
"stockCode": "HIJ",
"contractCode": "GE01905438831212",
"qtyEfek": "100"
}
},
"dueDate": "2019-08-06",
"stockType": "S",
"tradeDate": "2019-08-06"
}
I want the following result:
"collaterals":{
{
"stockCode":"ABC",
"contractCode":"GE01905438831212",
"qtyEfek":"900"
},
{
"stockCode":"DEF",
"contractCode":"GE01905438831212",
"qtyEfek":"1900"
},
{
"stockCode":"HIJ",
"contractCode":"GE01905438831212",
"qtyEfek":"100"
}
}
I'm using laravel 5.6
You can't get collaterals as an object since a json object has to be indexed, but you can get an array of objects by creating a numeric array from your associative one:
$data = json_decode($json, true);
$data['collaterals'] = array_values($data['collaterals']);
$json = json_encode($data);
This will result in { "brokerPartner": "XX", "collaterals":[{"stockCode":"ABC", ...}, {"stockCode":"DEF", ...}]} (most of the string ommited).

Printing nested associative array in PHP

I'm using this code $json_output = (json_decode($json, true)); to transform from JSON to an associative array in PHP.
The resulting array looks too compĺex to me, I need to print only some keys and values but they are nested and so far I haven't been able to do it, the examples I had follow for printing are too basic for this.
This is part of my JSON:
{
"project": {
"company": "Company Name SA de CV",
"name": "Project Name",
"files": [
{
"project-id": "666666",
"filenameOnDisk": "HH-ORG-CMD-GUI-File.docx",
"uploaded-date": "2018-01-29T21:20:56Z",
"private": "0",
"version-id": "3939061",
"status": "active",
"tags": [
{
"name": "OPD",
"id": "25047",
"color": "#9e6957"
}
],
"id": "3796128",
"last-changed-on": "2018-01-29T21:21:46Z",
"versions": [],
"uploaded-by-user-first-name": "Someone",
"uploaded-by-user-last-name": "Anything",
"name": "HH-ORG-CMD-GUI-GUIA_RAPIDA_PARA_CREAR_PROCESOS",
"size": "262747",
"category-name": "Instructivos"
},
{
"project-id": "666",
etc...,
},
When parsed looks like
How do I print (lets say) filenameOnDisk and id keys of the Files array.
I don't know how to get to that nested array.
echo $json_output['project']['files'][0]['project-id'];
echo $json_output['project']['files'][0]['filenameOnDisk'];
echo $json_output['project']['files'][0]['version-id'];
Or you could put it in a foreach loop using an array of values you want (as long as they're all in the 'files' array). Eg.
$wantedValues = array("project-id","filenameOnDisk","version-id");
foreach ($wantedValues as $value) {
echo $json_output['project']['files'][0][$value];
}
I Just needed to add a couple of lines at the code provided by #SeeSamRun in order to get the full "Files" array.
$filesArray = $json_output['project']['files'];
$filesSize = count($filesArray);
$wantedValues = array("project-id","filenameOnDisk","version-id");
for ($i=0; $i < $filesSize; $i++) {
foreach ($wantedValues as $value) {
echo $json_output['project']['files'][$i][$value];
}
}

PHP - object array into json

array of $setting['accountType'] :
$setting['accountType']['all'] = 'ALL';
$setting['accountType']['A1'] = 'VIP1';
$setting['accountType']['A2'] = 'VIP2';
PHP code to generate the object:
$object = new stdClass();
$myArray = array();
foreach ($setting['accountType'] as $key => $val)
{
$object->id = $key;
$object->desc = $val;
$myArray[] = $object;
}
$accountType = $myArray;
PHP code to format object into json:
json_encode(['accountType'=> [(object)$accountType]));
However, i get the output as below :
"accountType": [{
"0": {
"id": "A2",
"desc": "VIP"
},
"1": {
"id": "A2",
"desc": "VIP"
},
"2": {
"id": "A2",
"desc": "VIP"
}
}]
Problem 1: why $accountType only keep the last object when I loop through?
Problem 2: without the array key of $accountType [solved by using array_values($accountType)]
This is something that I am trying to achieve:
"accountType": [{
"id": "all",
"desc": "All "
}, {
"id": "A1",
"desc": "Normal"
}, {
"id": "A2",
"desc": "VIP"
}]
How to get the output as above?
You should use
json_encode(['accountType'=> $accountType]);
instead of
json_encode(['accountType'=> [(object)$accountType]]);
In your code you are putting $accountType inside another array that is why you are getting that result
Here is a Demo and Explanation
Edit: The entire code
$setting['accountType']['all'] = 'ALL';
$setting['accountType']['A1'] = 'VIP1';
$setting['accountType']['A2'] = 'VIP2';
$myArray = array();
foreach ($setting['accountType'] as $key => $val)
{
$object = new stdClass(); // Note: $object should be created inside the loop
$object->id = $key;
$object->desc = $val;
$myArray[] = $object;
}
$accountType = $myArray;
echo json_encode(['accountType'=> $accountType]);
And Here is the Revised Demo
Try this,
echo json_encode(array_values($your_array));
Let me know if its working
This is exactly the same thing, no?
The numerotation is displayed because you need it to access to the specific json object.
You have an array and you can access to each element by its key.

PHP Create Multidimension Array for JSON output

I feel terrible even asking because I have been TRYING to understand and comprehend other peoples examples this is what i'm TRYING to accomplish
{
"field": [
{
"appid": 0,
"page": 0,
"fieldname": "Sweet",
"value": "Tammy Howell"
},
{
"appid": 1,
"page": 1,
"fieldname": "Cecilia",
"value": "Shana Jordan"
},
{
"appid": 2,
"page": 2,
"fieldname": "Merritt",
"value": "Copeland Pena"
}
]
}
I need to be able to make the above JSON output happen when doing an SQL SELECT statement
Here is my currentCode
$x = 0;
$userFieldsResult = mysqli_query($database_id, "SELECT * FROM theDB.DynamicFields ORDER BY Page, Priority") or die (mysqli_error($database_id));
if (mysqli_num_rows($userFieldsResult)<=0)
{
echo "nothing found";
exit;
} else
{
while($row = mysqli_fetch_array($userFieldsResult))
{
$userFields[$x]['appid'] = $row['appid'];
$userFields[$x]['page'] = $row['page'];
$userFields[$x]['fieldname'] = $row['fieldname'];
$userFields[$x]['value'] = $row['value'];
$x++;
}
echo json_encode($userFields);
exit;
}
echo json_encode($userFields);
This is normally how i just been outputting json so they each are just part of 1 array looping, but I am trying to understand how to create more "in-depth" JSON arrays so they have a specific identifier before they list out the information.
[
{
"appid": 0,
"page": 0,
"fieldname": "Sweet",
"value": "Tammy Howell"
},
{
"appid": 1,
"page": 1,
"fieldname": "Cecilia",
"value": "Shana Jordan"
},
{
"appid": 2,
"page": 2,
"fieldname": "Merritt",
"value": "Copeland Pena"
}
]
In my example I just want to be able to have "field" be the "upper" part of the array that holds all the information as i showed in the example above.
You need to add field as parent array to your array.
Change from
echo json_encode($userFields);
Into
$json = array('fields' => $userFields);
echo json_encode($json);
In this particular configuration, you are being returned an object with properties, of which, the property's value may be an array.
So what you should actually be doing is creating a stdClass object in PHP and assigning it a property of "field" which is an empty array. Then you can push into this array stack.
$obj = new stdClass();
$obj->fields = array();
while(....){
$obj->fields[$x]['appid'] =
$obj->fields[$x]['appid'] = $row['appid'];
$obj->fields[$x]['page'] = $row['page'];
$obj->fields[$x]['fieldname'] = $row['fieldname'];
$obj->fields[$x]['value'] = $row['value'];
}
Now you can json encode the object and return it.
echo json_encode($obj);

Categories