Should JSON Api attributes element contain nested objects? - json

This is the first time we are using JSON API in our projects and according to specification on their web, this is what a regular JSON API response should look like
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever.",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z"
},
"relationships": {
"author": {
"data": {"id": "42", "type": "people"}
}
}
}],
"included": [
{
"type": "people",
"id": "42",
"attributes": {
"name": "John",
"age": 80,
"gender": "male"
}
}
]
}
We are not sure, should attributes in data be always flat, or attributes could also contain nested objects such as location for example
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!",
"body": "The shortest article. Ever.",
"created": "2015-05-22T14:56:29.000Z",
"updated": "2015-05-22T14:56:28.000Z",
"location":
{
"lat": "0.00",
"long": "0.00"}
},

Yes, take a look to : http://jsonapi.org/format/#document-resource-object-attributes
Complex data structures involving JSON objects and arrays are allowed
as attribute values. However, any object that constitutes or is
contained in an attribute MUST NOT contain a relationships or links
member, as those members are reserved by this specification for future
use.

After Decoding your JSON, Result is -
Array
(
[data] => Array
(
[0] => Array
(
[type] => articles
[id] => 1
[attributes] => Array
(
[title] => JSON API paints my bikeshed!
[body] => The shortest article. Ever.
[created] => 2015-05-22T14:56:29.000Z
[updated] => 2015-05-22T14:56:28.000Z
[location] => Array
(
[lat] => 0.00
[long] => 0.00
)
)
[relationships] => Array
(
[author] => Array
(
[data] => Array
(
[id] => 42
[type] => people
)
)
)
)
)
[included] => Array
(
[0] => Array
(
[type] => people
[id] => 42
[attributes] => Array
(
[name] => John
[age] => 80
[gender] => male
)
)
)
)
And here location contains an array, so this will be an nested object.

Related

API POST request in Julia

I am trying to convert some Python code to Julia. Here is the Python code:
url = "http://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101G/BefUtvKon1749"
json = {
"query": [
{
"code": "Kon",
"selection": {
"filter": "item",
"values": [
"1",
"2"
]
}
},
{
"code": "ContentsCode",
"selection": {
"filter": "item",
"values": [
"000000LV"
]
}
}
],
"response": {
"format": "px"
}
}
r = requests.post(url=url, json=json)
Below is the Julia code, that is not working, with this error message:
syntax: { } vector syntax is discontinued around path:8
top-level scope at population_data.jl:8
using DataFrames, DataFramesMeta, HTTP, JSON3
url = "http://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101G/BefUtvKon1749"
json = {
"query": [
{
"code": "Kon",
"selection": {
"filter": "item",
"values": [
"1",
"2",
"1+2"
]
}
},
{
"code": "ContentsCode",
"selection": {
"filter": "item",
"values": [
"000000LV"
]
}
}
],
"response": {
"format": "px"
}
}
r = HTTP.post(url, json)
My attempts to solve this are the following:
Convert the json variable to a string using """ around it.
Converting the JSON string to Julia data types, using JSON3.read()
Passing the converted JSON string to the POST request. This gives the following error:
IOError(Base.IOError("read: connection reset by peer (ECONNRESET)", -54) during request(http://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101G/BefUtvKon1749)
None of it works, and I am not even sure that it is about the JSON format. It could be that I am passing the wrong parameters to the POST request. What should I do?
One way of solving this consists in building the parameters as native julia data structures, and use JSON to convert and use them as the body of your PUT request:
Dictionaries in julia are built using a syntax like Dict(key => value). Arrays are built using a standard syntax: [a, b, c]. The julia native data structure equivalent to your parameters would look like this:
params = Dict(
"query" => [
Dict("code" => "Kon",
"selection" => Dict(
"filter" => "item",
"values" => [
"1",
"2",
"1+2"
]),
),
Dict("code"=> "ContentsCode",
"selection" => Dict(
"filter" => "item",
"values" => [
"000000LV"
]),
),
],
"response" => Dict(
"format" => "px"
))
Then, you can use JSON.json() to build the JSON representation of it as a string and pass it to the HTTP request:
using HTTP
using JSON
url = "http://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101G/BefUtvKon1749"
# send the request
r = HTTP.request("POST", url,
["Content-Type" => "application/json"],
JSON.json(params))
# retrieve the response body as a string
b = String(r.body)

Laravel: Resources with json field

I am on Laravel 7.x and I have two models (CustomerOrder composed of many CustomerOrderLines) with parent - child relationship. Parent (CustomerOrder) model has a json type field among its fields.
CustomerOrderResource.php:
return [
'id' => $this->id,
'wfx_oc_no' => $this->wfx_oc_no,
'qty_json' => json_decode($this->qty_json)
];
CustomerOrderLineResource.php:
return [
'id' => $this->id,
'description' => $this->description,
'customer-order' => $this->customerOrder
];
CustomerOrder->GET request returns properly formatted data as:
"data": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": {
"L": "20",
"M": "30",
"S": "20",
"XL": "100"
}
}
But for CustomerOrderLine->GET, the response is as:
"data": {
"id": 15,
"description": "test desc",
"customer-order": {
"id": 11,
"wfx_oc_no": 12,
"qty_json": "{\"L\": \"20\", \"M\": \"30\", \"S\": \"20\", \"XL\": \"100\"}"
}
}
json field is not properly formatted. It seems it doesn't go through Resource class. Please let me know, how can I get this fixed?
FYI
CustomerOrderLine.php:
public function parent()
{
return $this->belongsTo(CustomerOrder::class);
}
Finally managed to get it solved using json cast. The field was included in $casts array in the model.

Yii2 kartik depend dropdown doesnt load next drop down list

I am using kartik DepDropdown widget. When I choose the region it must load all the cities from it in the particular dropdown (e.g. the one with id city_id).
In the chrome network tab I can see that the action returns the expected result json format of all the cities
{
"output": {
"40": "Велико Търново",
"41": "Горна Оряховица",
"42": "Елена",
"43": "Златарица",
"44": "Лясковец",
"45": "Павликени",
"46": "Полски Тръмбеш",
"47": "Свищов",
"48": "Стражица",
"49": "Сухиндол"
},
"selected": ""
}
But they doesn't load in the #city_id dropdown. My view looks like:
<div class="col-sm-6">
<?= $form->field($model, 'region_id')->dropDownList(Region::getAllRegions(), ['id' => 'region-dd', 'prompt'=> ' - ' . Yii::t('app', 'Region') . ' - ']); ?>
</div>
<div class="col-sm-6">
<?= $form->field($model, 'city_id')->widget(DepDrop::classname(), [
'options'=>[
'id'=>'city-id'
],
'pluginOptions'=>[
'allowClear' => true,
'depends' => ['region-dd'],
'url' => Url::to(['/system-information/load-cities'])
]
]); ?>
</div>
My controller:
public function actionLoadCities()
{
$out = [];
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if ($parents != null) {
$region_id = $parents[0];
$out = City::getAllCities($region_id);
echo Json::encode(['output'=>$out, 'selected'=>'']);
return;
}
}
echo Json::encode(['output'=>'', 'selected'=>'']);
}
Thank you in advance!
You have to provide the name=>value pairs of the id and the text to be assigned for a single option, if you look into the DOCS you will see that you have to return the array like below
[
'out'=>[
['id'=>'<city-id>', 'name'=>'<city-name>'],
['id'=>'<city-id>', 'name'=>'<city-name>']
],
'selected'=>'<city-id>'
]
which means that the id will be provided with the index id and the text for the dropdown will be provided with the index name, and looking at the response you have provided your method getAllCities is returning the array like below
[
'id'=>'name',
'id'=>'name',
]
you didn't add the method City::getAllCities($region_id); which is returning the $out array but on the very basic level as per documentation it should look like below,
Note: Change the table and column names respectively, I assume you have the id and name columns for the cities, if you have any other column name for the city name like city_name then you must create the alias name for the city_name field in the query.
public function getAllCities($region_id){
$query = new \yii\db\Query;
$query->select('id, name')
->from('{{%city}}')
->where(['=', 'region_id', $region_id])
->limit(20);
$command = $query->createCommand();
return $command->queryAll();
}
This will return an array like below.
Array
(
[0] => Array
(
[id] => 40
[name] => Велико Търново
)
[1] => Array
(
[id] => 41
[name] => Горна Оряховица
)
[2] => Array
(
[id] => 42
[name] => Елена
)
[3] => Array
(
[id] => 43
[name] => Златарица
)
)
which will be encoded as JSON like below
[
{
"id": "40",
"name": "Велико Търново"
},
{
"id": "41",
"name": "Горна Оряховица"
},
{
"id": "42",
"name": "Елена"
},
{
"id": "43",
"name": "Златарица"
}
]
and eventually when encode by the line
echo Json::encode(['output'=>$out, 'selected'=>'']);
it will return
{
"output": [
{
"id": "40",
"name": "Велико Търново"
},
{
"id": "41",
"name": "Горна Оряховица"
},
{
"id": "42",
"name": "Елена"
},
{
"id": "43",
"name": "Златарица"
}
],
"selected": ""
}
Hope this helps.

Laravel Array & JSON Casting to Algolia

I am trying to send some data along to Algolia through the toSearchableArray. Any strings I have stored in my DB are sending along fine, but I hit a roadblock when trying to push nested JSON data along—the information is being sent as a string with characters escaped.
This is a sample of the nested object that I am storing in my table (MySQL with a JSON data type):
[
{
"id": 19,
"name": "Mathematics",
"short": "Math"
},
{
"id": 23,
"name": "Science",
"short": "Science"
},
{
"id": 14,
"name": "Health and Life Skills",
"short": "Health"
}
]
My model looks like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
public function toSearchableArray()
{
$data = $this->toArray();
$data['grades'] = explode(';', $data['grades']);
$data['units'] = explode(';', $data['units']);
return $data;
}
}
I get an output that looks like this:
array:22 [
"objectID" => 1
"name" => "Resource #1"
"slug" => "resource-1"
"write_up" => """
This is an example write up.
"""
"author" => "johnny"
"type_name" => "Lesson Plan"
"language" => "English"
"grades" => array:3 [
0 => "Kindergarten"
1 => "Grade 1"
2 => "Grade 4"
]
"subjects" => "[{"id": 19, "name": "Mathematics", "short": "Math"}, {"id": 23, "name": "Science", "short": "Science"}, {"id": 14, "name": "Health and Life Skills", "short": "Health"}]"
"units" => array:2 [
0 => "Unit A"
1 => "Unit B"
]
"main_image" => "https://dummyimage.com/250x325/000000/fff.png&text=Just+a+Test"
"loves" => 88
"downloads" => 280
"created_at" => "2018-01-01 13:26:47"
"updated_at" => "2018-01-02 10:10:32"
]
As you can see, the 'subjects' attribute is being stored as a string. I know there is attribute casting in 5.5 (I am running 5.5), but I am not too clear on how I would implement the example they have for Array & JSON Casting in my work above. https://laravel.com/docs/5.5/eloquent-mutators#attribute-casting
Would anyone be willing to show me an example?
I'd rely on Attribute Casting for this, add a $casts property in your model and it will be done automatically.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;
class Resource extends Model
{
use Searchable;
protected $primaryKey = 'objectID';
protected $casts = [
'subjects' => 'array',
];
public function toSearchableArray()
{
// Same function as you posted
}
}
You can also do it manually in your toSearchableArray method with $data['subjects'] = json_decode($this->subjects, true);
I answered with more details on this other posts: https://discourse.algolia.com/t/laravel-array-json-casting-to-algolia/4125/2

Deserialize multidimensional JSON API Response with JSMSerializerBundle

I work with Symfony2/JSMSerializerBundle.
Serializing flat json objects to PHP objects works great. But the API I use, gives a multidimensional Json response:
{
"Webmessage": {
"#version": "1.0",
"Header": {
"Country": "NL",
"Language": "NL"
},
"Content": {
"Filters": {
"Sizes": {
"Size": [
{
"#id": "241",
"#text": "3,5"
},
{
"#id": "55",
"#text": "36"
}
]
},
"Colours": {
"Colour": [
{
"#id": "159",
"#text": "wit"
},
{
"#id": "54",
"#text": "zwart"
}
]
}
}
}
}
}
As deserialized PHP I want something like this:
Array
(
[sizes] => Array
(
[0] => AppBundle\Entity\Filter Object
(
[id:AppBundle\Entity\Filter:private] => 1
[text:AppBundle\Entity\Filter:private] => Heren
)
[1] => AppBundle\Entity\Filter Object
(
[id:AppBundle\Entity\Filter:private] => 2
[text:AppBundle\Entity\Filter:private] => Dames
)
)
[colour] => Array
(
[0] => AppBundle\Entity\Filter Object
(
[id:AppBundle\Entity\Filter:private] =>56
[text:AppBundle\Entity\Filter:private] => Black
)
[1] => AppBundle\Entity\Filter Object
(
[id:AppBundle\Entity\Filter:private] => 212
[text:AppBundle\Entity\Filter:private] => Yellow
)
)
)
Who has tips how i can do this?
Thanks!
Maybe you can decode it first and then use the Normalizer to create the entities. Something like this:
$array= json_decode($json, true);
$valueToDenormalize = $array['value'];
$normalizer = new GetSetMethodNormalizer();
$entity = $normalizer->denormalize($valueToDenormalize, 'Your\Class');
Be aware, I have not tried this. I don't know if the normalizer will work this way, but I know it's used to normalize and denormalize between arrays and Symfony's entities.
For further investigation you could take a look at the Serializer docs:
http://symfony.com/doc/current/components/serializer.html
Or the Normalizer:
http://api.symfony.com/2.3/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.html
Here's something about json_encode:
http://php.net/manual/en/function.json-decode.php