Lost In Translation: Issue Parsing JSON to Object in Flutter - json

Background: Project uses Yelp API in JSON to sift through business data. Two types of endpoints are use - https://www.yelp.com/developers/documentation/v3/business_search and https://www.yelp.com/developers/documentation/v3/business. The first part of this projects works as expected where the business_search api is parsed, saved as a list of strings and converted to a Business objects when it is needed to be displayed. The JSON -> Object code is as follows:
factory Business.fromJSON(Map<String, dynamic> json) {
return Business(
rating: json['rating'],
price: json['price'],
phone: json['phone'],
id: json['id'],
name: json['name'],
alias: json['alias'],
isClosed: json['is_closed'],
reviewCount: json['review_count'],
url: json['url'],
imageUrl: json['image_url'],
);
}
The code works when a call is converted from the business/search endpoint, with the JSON printout being:
json = {rating: 4.5, price: $$, phone: +16316751500, id: D5dS2-8JXhZxo3BzMLV3xQ, name: Ichi sushi & ramen, alias: ichi-sushi-and-ramen, is_closed: false, review_count: 128, url: https://www.yelp.com/biz/ichi-sushi-and-ramen?adjust_creative=Aj73ii92JG9IDLNvRyn_Ow&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_search&utm_source=Aj73ii92JG9IDLNvRyn_Ow, image_url: https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg}
The Issue: I encounter the following exception
[VERBOSE-2:ui_dart_state.cc(166)] Unhandled Exception: FormatException: Unexpected character (at character 2)
{id: D5dS2-8JXhZxo3BzMLV3xQ, alias: ichi-sushi-and-ramen...
^
when this response from business/details is called, saved and attempted to be converted to objects:
json = {id: D5dS2-8JXhZxo3BzMLV3xQ, alias: ichi-sushi-and-ramen, name: Ichi sushi & ramen, image_url: https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg, is_claimed: true, is_closed: false, url: https://www.yelp.com/biz/ichi-sushi-and-ramen?adjust_creative=Aj73ii92JG9IDLNvRyn_Ow&utm_campaign=yelp_api_v3&utm_medium=api_v3_business_lookup&utm_source=Aj73ii92JG9IDLNvRyn_Ow, phone: +15836751500, display_phone: (583) 675-1500, review_count: 128, categories: [{alias: sushi, title: Sushi Bars}, {alias: ramen, title: Ramen}], rating: 4.5, location: {address1: 200 Country Rd, address2: null, address3: , city: Bayshore, zip_code: 11803, country: US, state: NY, display_address: [200 Country Rd, Bayshore, NY 11803], cross_streets: }, coordinates: {latitude: 40.92842, longitude: -73.116}, photos: [https://s3-media1.fl.yelpcdn.com/bphoto/Phib_AwTkdD_kBV8r7179Q/o.jpg, https://s3-media2.fl.yelpcdn.com/bp<…> (*My note: debugger truncates with <...>*)
What is going wrong here? I have been trying many (and I mean many) different cases to rule out what the issue is but cannot find anything that is blatantly amiss. The only difference I can see is that the first type of call (business/search) starts with "rating" and contains fewer fields while the second type of call (business/details) returns much more data (note that I do not need much of it and hence do not need it in the Business object, but the nature of Yelp's api requires I do it in this way) and starts with the "id" field instead. Are these differences to blame and what does this error mean? Can you "plug in" a JSON to an object that has more fields than needed properties as I would like to keep the Business object as versatile as possible? Regardless, how could this be resolved? If more info is needed, please let me know. Thank you for your help and time!

After further analyzing the code, #KhashayarMotarjemi's suggestion tipped me off on a possible problem: the way the data was encoded. To save the data, I ran the the JSON through the toString() method, stripping the data of all of its " " and making it unable to be correctly parsed as JSON later on when retrieved from memory. The correct method would be jsonEncode(yourData) as this converts it to a string to be saved, but preserves the format needed when decoded down the line. So to anyone reading, if you are trying to store your JSON as a string, you must use jsonEncode() and not toString() when doing so. I hope this can help someone in the future!

Related

Message examples in OpenAPI 3 YAML specification

OpenAPI v3 offers using JSON or YAML format.
I prefer using YAML to create API specifications. YAML's readability is much better for me as an API designer.
However, sometimes I need to embed a message example whose type is object. I usually have the examples in JSON format. So I would do the following:
examples:
singlePet:
summary: Single pet
description: A request containing a single pet
value: |
{
"pets" : [
{
"petType" : "DOG",
"name" : "Ben"
}
]
}
However, such value is a string, not an object, whereas OpenAPI expects an object.
Do we have any options to embed a JSON as an example value in the YAML specification?
Currently, I convert the JSON to YAML and embed the YAML:
examples:
singlePet:
summary: Single pet
description: A request containing a single pet
value:
pets:
- petType: DOG
name: Ben
YAML is a superset of JSON. This means you can use JSON syntax for objects and arrays within a YAML document.
In your first example, you can just remove the | after the value: to keep the example value as an object.
The following are equivalent:
examples:
singlePet:
summary: Single pet
description: A request containing a single pet
value:
{
"pets" : [
{
"petType" : "DOG",
"name" : "Ben"
}
]
}
examples:
singlePet:
summary: Single pet
description: A request containing a single pet
value:
pets:
- petType: DOG
name: Ben
Alternatively, you can use externalValue to point to an external file containing sample JSON.
examples:
singlePet:
summary: Single pet
description: A request containing a single pet
externalValue: 'https://api.example.com/docs/examples/pet.json'

Angular 13 - How to get json data into a useable format

I'm new to Angular and am struggling to build my first app. I have a web service which returns some json in this format:
<PolicyHolderClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://tempuri.org/">
<Title>Mr</Title>
<Forename>J</Forename>
<Surname>Smith</Surname>
<Address1>21 Somewhere Street</Address1>
<Address2>Hereton</Address2>
<Address3>Theresville</Address3>
<Address4/>
<Address5/>
<Postcode>TH8 6AB</Postcode>
<Telephone>01234567890</Telephone>
<Email>name#provider.co.uk</Email>
</PolicyHolderClass>
I want to have some code which will get the data from the web service into a format I can use. I have created an interface
interface PHDETAILS {
Title: string,
Forename: string,
Surname: string,
Address1: string,
Address2: string,
Address3: string,
Address4: string,
Address5: string,
Postcode: string,
Telephone: string,
Email: string
}
and I have this code to retrieve the data
const phDetails: PHDETAILS = this.http.get('HelloWorld?PolicyNumber=' + policyNumber.toString());
Unfortunately this isn't working, and i'm getting this in the console
error TS2740: Type 'Observacle<Object>' is missing the following properties from type 'PHDETAILS': Title, Forename, Surname, Address1, and 7 more.
So, can anyone tell me how I get the json data into phDetails? I am using Angular 13. I would prefer a simple approach as i'm a newbie, but it can be quick and dirty as long as it gets the job done. Thanks.

Should I write out the JSON structure of an api call as an interface in typescript to interact with values inside response?

So i'm just starting out with typescript and something that I can't find much info on is how to deal with types for api call responses.
Lets say, I make a GET request to an api and it returns a JSON object for example:
{
name: "john",
age: 12
}
in my code, if i wanted to interact with response.name would i have to create an interface like below to use response.name without having a red linter under it?
interface Response {
name: string,
age: number
}
or is there a easier way to do it as some api calls would return JSON > 10 lines long and copying out the whole structure for every type of call seems very troublesome. Another thought I had was to create the interface but only have values I would use instead of the whole structure, but i'm not too sure so any help would be greatly appreciated!
You can define Response as a "common" type, that supports all types of API json response.
interface IResponse {
[key: string]: any
}
Now, you can type response.name without the "red line", also response.not_exist_property is valid.
My recommendation is to define all types for all API response type:
interface GetUserResponse {
name: string,
age: number,
}
for GET /users/:id (example)
You can use this tool to convert a json response to Typescript type.

How to deal with this kind of map data

{productid: 456813, token: 6t90jwawirc3dznbn8khevkg1b6n7vnx, quote_id: 813, specData: {{super_attribute: {218: {value: 297}}, qty: 1}, {super_attribute: {218: {value: 298}}, qty: 1}, {super_attribute: {218: {value: 299}}, qty: 1}, {super_attribute: {218: {value: 300}}, qty: 1}, {super_attribute: {218: {value: 301}}, qty: 1}}}
print(jsonEncode(params));
Got the following error
Converting object to an encodable object failed: Instance of '_CompactLinkedHashSet<dynamic>'
Your data structure is not valid for directly converting to JSON. The main problem is that you have a Set in params (specData) which is not allowed for jsonEncode which is documented with:
If value contains objects that are not directly encodable to a JSON string (a value that is not a number, boolean, string, null, list or a map with string keys), the toEncodable function is used to convert it to an object that must be directly encodable.
There are two solutions here. You can either change specData to be a List. Or you can use the toEncodable argument to jsonEncode to change the type of your Set to List when converting to JSON:
print(jsonEncode(params, toEncodable: (object) {
if (object is Set) {
return object.toList();
} else {
throw Exception('Could not encode: $object');
}
}));
Bonus: It is difficult to know if your key's in super_attribute is int or String. But you must use String if you want to convert to JSON. If it is int you can also use the toEncodable to fix that but it will get a little messy so it is best if you can get some control over your data structure.

Lua access indeces from table generated from JSON

So, I am bound to use Lua for getting weather data from the Openweathermap API.
I managed to send a http request to return and store all data but now I am stuck with a Lua table I don't know how work with. I am very new to Lua and I didn't find any guide or similar regarding such a deep nested table in Lua.
In particular I am just interested in the field called temp in main. Here is a sample response from the API: Sample request response
The dependencies are Lua's socket.http and this json to Lua table formatter.
Here is my basic code structure
json = require ("json")
web = require ("socket.http")
local get = json.decode(web.request(<API Link>))
"get" now stores a table I don't know how to work with
With the help of https://www.json2yaml.com/, the structure is:
cod: '200'
message: 0.0036
cnt: 40
list:
- dt: 1485799200
main:
temp: 261.45
temp_min: 259.086
temp_max: 261.45
pressure: 1023.48
sea_level: 1045.39
grnd_level: 1023.48
humidity: 79
temp_kf: 2.37
weather:
- id: 800
main: Clear
description: clear sky
icon: 02n
clouds:
all: 8
wind:
speed: 4.77
deg: 232.505
snow: {}
sys:
pod: n
dt_txt: '2017-01-30 18:00:00'
…
- dt: 1486220400
…
city:
id: 524901
name: Moscow
coord:
lat: 55.7522
lon: 37.6156
country: none
So,
for index, entry in ipairs(get.list) do
print(index, entry.dt, entry.main.temp)
end
ipairs iterates over the positive integer keys in the table, up to but not including the first integer without a value. It seems that's the way the JSON library represent a JSON array.
If you don't know how to work with Lua tables you probably should learn the very basics of Lua. Refer to https://www.lua.org/start.html
The json string encodes a Lua table with all it's keys and values.
You can either read how the encoder encodes a table or you simply encode your own table and analyze the resulting json string.
print(json.encode({1,2,3}))
[1,2,3]
print(json.encode({a=1, b={1,2}, [3]="test"}))
{"3":"test","b":[1,2],"a":1}
and so on...
There's always table keys and values, separated by a colon.
Values can be numbers, strings, tables...
If the table only has numeric keys starting from one the value is a list of those values in brackets. If you have different keys in a table its encapsulated in curly brackets...
So let's have a look at your results. I'll remove 39 of the 40 entries to shorten it. I'll also indent to make the structure a bit more readable.
{
"cod":"200",
"message":0.0036,
"cnt":40,
"list":[{
"dt":1485799200,
"main":{
"temp":261.45,
"temp_min":259.086,
"temp_max":261.45,
"pressure":1023.48,
"sea_level":1045.39,
"grnd_level":1023.48,
"humidity":79,
"temp_kf":2.37},
"weather":[
{
"id":800,
"main":"Clear",
"description":"clear sky",
"icon":"02n"
}],
"clouds":{"all":8},
"wind":{"speed":4.77,"deg":232.505},
"snow":{},
"sys":{"pod":"n"},
"dt_txt":"2017-01-30 18:00:00"}
],
"city":{
"id":524901,
"name":"Moscow",
"coord":{
"lat":55.7522,
"lon":37.6156
},
"country":"none"
}
}
That sample response appears to have many subtables that have a main in them. Try this: get.list[1].main.temp.
After 2 days I finally found the error. I was working in a Minecraft Mod called OpenComputers which utilizes Lua. It seems like the mod uses a own version of socket.http and every time I wanted to print the response it returned two functions to use with request. I found out that if I put a "()" after the variable it returned the Response as a String and with the JSON library I could decode it into a workable table.
Side note: I could access the weather like this: json_table["weather"]["temp"]
The mod is pretty poorly documented on the http requests so I had to figure this out by myslef. Thanks for your responses, in the end the error was as always very unexpected!