json_encode() on a multidimensional array - with string keys - php

I am creating an very large multidimensional array using PHP. Each object contains Name, ID, ParentID and Children. Children is an array of more objects in the same format.
It is critical I name the IDs of each object - this helps me put each object under the correct parent. (In the code below, I use 101, 102 etc.)
However, the problem I am having is when I return the array in JSON using json_encode. Each 'Children' array is being printed as an object, not an array - as shown in the JSON code below.
As I read on another SO thread here, they "are made as objects because of the inclusion of string keys" - although they are numbers, they are still strings.
{
"101": {
"ID": "101",
"ParentID": "0",
"Name": "Root One"
"Children": {
"102": {
"ID": "102",
"ParentID": "101",
"Name": "Child One"
},
"103": {
"ID": "103",
"ParentID": "101",
"Name": "Child Two",
"Children": {
"104": {
"ID": "104",
"ParentID": "103",
"Name": "Child Child One"
}
}
},
Does anyone know how to overcome this issue?
Edit: The JSON should look like this (the square brackets are important!):
[
{
"ID": "101",
"ParentID": "0",
"Name": "Root One",
"Children": [
{
"ID": "102",
"ParentID": "101",
"Name": "Child One",
"Children": [

A JSON array has no explicit indexes, it's just an ordered list. The only JSON data structure that has named keys is an object. The literal should make this quite obvious:
["foo", "bar", "baz"]
This array has no named indices and there isn't any provision to add any.
PHP conflates both lists and key-value stores into one array data type. JSON doesn't.

This is your object:
$parent=new StdClass();
$parent->ID=101;
$parent->ParentID=0;
$parent->Name='Root One';
$child1=new StdClass();
$child1->ID=1011;
$child1->ParentID=$parent->ID;
$child1->Name='Child One';
$parent->Children[]=$child1;
$child1_1=new StdClass();
$child1_1->ID=10111;
$child1_1->ParentID=$child1->ID;
$child1_1->Name='Child One One';
$child1->Children[]=$child1_1;
This is your JSON convert function:
echo json_encode($parent,JSON_PRETTY_PRINT);
and this is your object coded into JSON format:
{
"ID": 101,
"ParentID": 0,
"Name": "Root One",
"Children": [
{
"ID": 1011,
"ParentID": 101,
"Name": "Child One",
"Children": [
{
"ID": 10111,
"ParentID": 1011,
"Name": "Child One One"
}
]
}
]
}
The answer came later because I started learning PHP later. Anyway, some day, someone might find it useful.

I have now got a working solution which is fast and works well.
Firstly, as written in SO link from the question;
In JSON, arrays only have numeric keys, whereas objects have string
properties. The inclusion of a array key forces the entire outer
structure to be an object by necessity.
In JSON; Curly braces hold objects ({}), Square brackets hold arrays ([]).
So using a string as a key will result in the json_encode function returning objects, whereas reseting the keys will ensure it creates arrays.
Therefore, just before I return my JSON encoded string, I run a function to reset all the array keys. The code I found on this SO thread (Reset array keys in multidimensional array) was particularly useful!

http://php.net/manual/en/function.json-decode.php
Set 2nd parameter of json_decode to true to atleast get assoc arrays.
Aside from that: javascript can't handle non-sequential/non-contiguous array indexes, so as soon as the id's are not sequential, json has no other option then to convert it into "string" indexes.

Related

Invalid JSON format in using array_unique function [duplicate]

This question already has answers here:
json_encode PHP array as JSON array not JSON object
(4 answers)
Closed 3 years ago.
I have removed the duplicate items for the following array, by writing:
$outcome['id'] = array_unique($outcome['id'], SORT_REGULAR);
but I'm getting undesirable JSON output
"id": {
"0": {
"id": 947,
"label": "ABCD"
},
"1": {
"id": 2175,
"label": "EFGH"
},
"2": {
"id": 15,
"label": "IJKL"
}
}
instead of the below , which is the desirable JSON output :
"id": [
{
"id": 947,
"label": "ABCD"
},
{
"id": 2175,
"label": "EFGH"
},
{
"id": 15,
"label": "IJKL"
}
]
While debugging on PHPStorm, the result shown was in array format, but on Postman, the result is being transformed to object!
array_unique preserves keys while removing items. This means it'll remove some array entries but keep indexes of remaining items intact, resulting in non-continuous numerical indices (gaps). An array with gaps in their numerical indices or an array with non-numerical indices counts as an associative array which in JSON becomes a JSON object. You can reset the indices by using array_values like e.g.
$outcome['id'] = array_values(array_unique($outcome['id'], SORT_REGULAR));

Convert Single Object into an Array containing that Object

I have a number of JSON files and when there is a single object in them, their datatype is inconsistent. I am trying to alter the files that contain one item so that the element I am referencing is always an array.
Here's an example of a 'good' JSON file:
{
"apiv2": {
"items": [{
"id": "00001",
"name": "ITEM 1"
}, {
"id": "00002",
"name": "ITEM 2"
}]
}
}
In PHP terms, $json->apiv2->items is always an array I can apply the same functions to.
Every so often I have a JSON file that contains one item:
{
"apiv2": {
"items": {
"id": "00003",
"name": "ITEM 3"
}
}
}
When I attempt to iterate through 'items' with the same functions, they fail as it is now an object instead of an array.
My goal is to alter the JSON data and rewrite the file so the single item files are consistent with the multiple item files:
{
"apiv2": {
"items": [{
"id": "00003",
"name": "ITEM 3"
}]
}
}
Maybe it's because it's Friday afternoon, but I can't seem to wrap my head around this one. I thought it would be as simple as:
$json->apiv2->items = (array) $json->apiv2->items;
But that just turns it into an array with two elements, "id" and "name", not one element with the object.
As I said in the comments
When you do
$json->apiv2->items = (array) $json->apiv2->items;
PHP will convert $items to an actual array [ "id" => "00003", "name" => "ITEM 3"]
Which will give you the results ['items' => [ "id" => "00003", "name" => "ITEM 3"]]
Instead of converting your object, you need to wrap it
$json->apiv2->items = [$json->apiv2->items];
More advanced: since sometimes items can be an array and sometimes not, you can make a function [1] to wrap them
function wrap($value)
{
if (is_null($value)) {
return [];
}
return is_array($value) ? $value : [$value];
}
$json->apiv2->items = wrap($json->apiv2->items);
POC : https://3v4l.org/7p9b0
[1] Stolen from Laravel helpers
Use json_decode() and access it like this:
$json = '{"apiv2":{"items": [{"id": "00001", "name": "ITEM 1" }, {"id": "00002", "name": "ITEM 2" }]}}';
print_r(json_decode($json, true)['apiv2']['items']);

Processing a JSON response from TFL api using PHP?

I am trying to get certain information from the following JSON response from an API. This is the actual API call.
{
"$type": "Tfl.Api.Presentation.Entities.PlacesResponse, Tfl.Api.Presentation.Entities",
"centrePoint": [
51.555,
0.059
],
"places": [{
"$type": "Tfl.Api.Presentation.Entities.StopPoint, Tfl.Api.Presentation.Entities",
"naptanId": "490009219W",
"indicator": "Stop B",
"stopLetter": "B",
"modes": [
"bus"
],
"icsCode": "1009219",
"stopType": "NaptanPublicBusCoachTram",
"stationNaptan": "490G00009219",
"lines": [{
"$type": "Tfl.Api.Presentation.Entities.Identifier, Tfl.Api.Presentation.Entities",
"id": "25",
"name": "25",
"uri": "/Line/25",
"type": "Line"
}, {
"$type": "Tfl.Api.Presentation.Entities.Identifier, Tfl.Api.Presentation.Entities",
"id": "86",
"name": "86",
"uri": "/Line/86",
"type": "Line"
}, {
"$type": "Tfl.Api.Presentation.Entities.Identifier, Tfl.Api.Presentation.Entities",
"id": "w19",
"name": "W19",
"uri": "/Line/w19",
"type": "Line"
}],
"lineGroup": [{
"$type": "Tfl.Api.Presentation.Entities.LineGroup, Tfl.Api.Presentation.Entities",
"naptanIdReference": "490009219W",
"stationAtcoCode": "490G00009219",
"lineIdentifier": [
"25",
"86",
"w19"
]
}],
"lineModeGroups": [{
"$type": "Tfl.Api.Presentation.Entities.LineModeGroup, Tfl.Api.Presentation.Entities",
"modeName": "bus",
"lineIdentifier": [
"25",
"86",
"w19"
]
}],
"status": true,
"id": "490009219W",
"commonName": "Little Ilford Lane",
"distance": 64.10041498232529,
"placeType": "StopPoint",
"additionalProperties": [{
"$type": "Tfl.Api.Presentation.Entities.AdditionalProperties, Tfl.Api.Presentation.Entities",
"category": "Direction",
"key": "CompassPoint",
"sourceSystemKey": "Naptan490",
"value": "W"
}, {
"$type": "Tfl.Api.Presentation.Entities.AdditionalProperties, Tfl.Api.Presentation.Entities",
"category": "Direction",
"key": "Towards",
"sourceSystemKey": "CountDown",
"value": "East Ham or Manor Park"
}],
"lat": 51.554475,
"lon": 0.059381
}]
}
I want to get the naptanId, line identifier and towards as name value pairs and print them. For example,
naptan :490009219W;
lineidentifer Whatever the values are
towards : "Eastham or manor park"
Please help me I am a beginner,
Thank you in advance
Based on your question, it seems you are only interested in the values from the first place returned, so I'll assume that in the answer.
To get the data from the API into an object structure (as opposed to the JSON string you initially get):
$data = json_decode(file_get_contents('https://api.tfl.gov.uk/Place?lat=51.555504&lon=0.0592359&radius=200&includeChildren=False&type=NaptanPublicBusCoachTram&app_id=&app_key='), true);
To get the naptanId:
$naptanId = $data['places'][0]['naptanId'];
To get the lineIdentifier, which I'll assume is the one from the first lineGroup element (not lineModeGroups, but you can change as appropriate):
$lineIdentifier = $data['places'][0]['lineGroup'][0]['lineIdentifier'];
This value will be an array.
To get the towards value is a bit more complex, because you need to filter the additionalProperties array based on a child key, then pull out the value; again I'm assuming you're only interested in the first value:
$towards = array_values(array_filter(
$data['places'][0]['additionalProperties'],
function ($property) {
return $property['key'] === 'Towards';
}
))[0]['value'];
The call to array_filter pulls out the additionalProperties element where key === 'Towards' (note this assumes case sensitivity, you could add a strtolower and change the string literal to 'towards' if you want to match case insensitively). The call to array_values is necessary to normalise the array indexes. Then we pull out the first element [0] and get its value property.
Note that the above code does not do any error checking, for example if the API call does not return any places then the first time you do $data['places'][0] it will fail. If you need to handle these cases (which you almost certainly do), then you will need to pre-check these things, e.g.:
if (!isset($data['places']) || sizeof($data['places']) === 0) {...}

php loop through json, differing property names

I'm not to comfortable with json, and there has to be an easier way of doing what I am doing.
Here is a sample of the json feed I'm working with.
"goalie": [
{
"position": "Goalie",
"id": 8476945,
"weight": 207,
"height": "6' 4\"",
"imageUrl": "http://3.cdn.nhle.com/photos/mugs/8476945.jpg",
"birthplace": "Commerce, MI, USA",
"age": 22,
"name": "Connor Hellebuyck",
"birthdate": "May 19, 1993",
"number": 30
}
],
"defensemen": [
{
"position": "Defenseman",
"id": 8470834,
"weight": 260,
"height": "6' 5\"",
"imageUrl": "http://2.cdn.nhle.com/photos/mugs/8470834.jpg",
"birthplace": "Roseau, MN, USA",
"age": 30,
"name": "Dustin Byfuglien",
"birthdate": "March 27, 1985",
"number": 33
}
]
There is a lot more data than shown above, multiple goalies, defensemen and forwards. Currently I'm using a for loop to loop through goalies, then another one to loop through defensemen and so on. Is there a way to loop through every player regardless of the property name, not sure if that's the right term... if not please correct me.
Thanks
Yes there is! In PHP, looping over objects ( or associative arrays, or dictionaries ) is done in the same way as looping over arrays ( indexed arrays, or lists ). So nested looping is your friend. Let's say your huge data structure was saved to a variable called $everyone
<?php
foreach ($everyone as $playergroup => $players) {
foreach ($players as $player) {
// now you can operate on each player
}
}
?>

Remove the {1} from an array of objects

I want my $row->responses object to look like so:
"responses": [
{
"id": 1381822,
"user_id": 45313,
"respondent_name": "JP Pullen",
"response_time": 1424127673,
"is_root_response": true,
"response_tree_id": 1377018,
"project_users_id": 74311,
"display_name": "Mobile 2",
"lft": 1,
"rgt": 2,
"response_read": true,
"body": "Sure it really is was great I purchased choclate at my local grocery store.",
"answers": [ ],
"avatar_url": null,
"mtime": null,
"media_list": [ ]
},
Yet my $row->responses object looks like:
"responses": {
"1": {
"id": 1381825,
"user_id": 45167,
"respondent_name": "First Name Last Name",
"response_time": 1424128177,
"is_root_response": true,
"response_tree_id": 1377021,
"project_users_id": 74312,
"display_name": "SimonSays",
"lft": 3,
"rgt": 4,
"response_read": false,
"body": "What's up!",
"answers": [ ],
"avatar_url": "https:\/\/portalvhds5kcv8nfhdz8zn.blob.core.windows.net\/user-45167\/avatar-50x50",
"mtime": 1420206441,
"media_list": [ ]
}
}
How do you get rid of the stupid {1} in the object. I didn't get it until I had to loop through the objects and unset one of them. I am not a PHP expert. Strongly Typed FTW lol.
foreach($row->responses as $elementKey => $element)
^ That is how I looped through the responses object to unset one:
unset($row->responses{$elementKey});
When arrays are encoded using json_encode() PHP will determine whether it should use array syntax (square brackets) or object syntax (curly brackets). In this case, the first array index is 1 instead of 0 so it can't reliably be encoded as a JavaScript array.
To prevent this from happening, you can use array_values() to renumber the array to be 0-based again:
$row->responses = array_values($row->responses);

Categories