Flutter: how to decode this complex JSON string - json

From my REST API a JSON string is received as
{"total":"30","results":[
{"ID":"1809221034017","DATE":"2018-09-22","REG":"(E9)","START":"10:40","END":"10:48"},
{"ID":"1809221337250","DATE":"2018-09-22","REG":"(E4)","START":"13:43","END":"13:57"},
{"ID":"1809161032213","DATE":"2018-09-16","REG":"(E1)","START":"11:04","END":"11:13"}]}
The total field tells me that the database contains in total 30 records, the requested data (only 3 rows) is included in the results section.
I need to parse the data, so I can show the results in ListView. I managed to do this with a simple JSON string, but not with this complex JSON string. Unfortunately I am not able to change the output of the web service since this is hosted by a 3rd party.
Any help, or a code example, is appreciated.
Thanks in advance

Read first my other answer here.
Then I suggest you to use a class generation library like quicktype.
Using quick type for example you can easily and automatically genearate your moidel class in dart using your JSON. Here the generated file.
quicktype --lang dart --all-properties-optional https://www.shadowsheep.it/so/53968769/testjson.php -o my_json_class.dart
Then use it in code:
import 'my_json_class.dart';
import 'package:http/http.dart' as http;
var response = await http.get('https://www.shadowsheep.it/so/53968769/testjson.php');
var myClass = MyJsonClass.fromJson(jsonDecode(response.body));
for(var result in myClass.results.toList()) {
print(result?.id);
}
N.B. If you'll master a code generator library, then you'll be able to parse any type of JSON coming from a REST API and you'll have more time for fun.

So here we can see that there is a JSON object having two values a JSON array and a total number.Sometimes the internet services don't return the full value but only a part due to network issues.
So now in the array we have number of results objects with an ID,DATE,REG,START,END.
If you format the JSON you will decode it easily.

i recommend this article: https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51
class Result
{
String id ;
String date;
String reg ;
String start;
String end ;
Result({this.date,this.end,this.id,this.reg,this.start});
factory Result.fromJson(Map<String, dynamic> parsedJson) {
return new Result(
id: parsedJson['ID'],
date: parsedJson['DATE'],
reg: parsedJson['REG'],
start: parsedJson['START'],
end: parsedJson['END'],
);
}
}
class Results
{
String total;
List<Result> results;
Results({this.results, this.total});
factory Results.fromJson(Map<String, dynamic> parsedJson) {
var list = parsedJson['results'][] as List;
return new Results(
total: parsedJson['total'],
results: list.map((i) => Result.fromJson(i)).toList());
}}

Related

I think parsing this type of json response is impossible in Dart. How can I convert this json to Dart Class?

An API gives me json response like this:
[{"version": "v3.5"}, {"setup": true}, {"address": "e7d398b"}, {"connected": true}, {"active": true}, {"id": "ce7143"}, {"genuine": true}]
As you can see, this is a list of objects. I tried parsing it like this using quicktype generated model class-
List<Result>.from(result.map((x) => Result.fromJson(x)));
But it's failing since each of the objects are of different types.
I think I have to convert each of the objects in the array one by one to Dart classes and add it to an Array.
So I tried this (I am using dio) -
final result = response.data;
var b = List.empty(growable: true);
result.map((x) => b.add(x));
But it's not working.
How can I atleast access the elements of the array?
Solved
Inspired by the accepted answer, I was able to generate corresponding Dart Class. Never thought can looping through a map is possible, IDE was not giving any clue.
final result = response.data;
Map<String, dynamic> map = {};
for (var e in result) {
map.addAll(e);
}
final finalResult = Result.fromJson(map);
return finalResult;
As Randal Schwartz mentioned above, there is no JSON you can not parse with Dart.
In your case, you have a List of Map objects. What you can do is:
final data = jsonDecode(json) as List;
Map<String, dynamic> map = {};
for (var e in data) {
map.addAll(e);
}
print(map);
//prints
{version: v3.5, setup: true, address: e7d398b, connected: true, active: true, id: ce7143, genuine: true}
If you're using the dio flutter package it returns decoded json, no
need to call for jsonDecode.
I recommend using json code generation if you face large json instead of relying on quicktype generated models.
There's no JSON that isn't parsable with Dart. But you might end up with a data structure that requires careful navigation. Classes make it easier, but there isn't always a class structure for any arbitrary JSON, and maybe that's your point. In that case, you'll have to navigate to the data of interest in an ad-hoc fashion.

Encode an objects to json, store it in sharedprefs, then retrieve it as string

In my app, the user chooses the chapter he wants to read, the verse he wants to begin from, and the end verse.
I'm going to store these three strings and show in his "reading history" list, where he can see all of his previous readings.
I read that you can do that by creating a class, storing these in an object and converting it to JSON then storing it inside sharedprefs.(or something like that).
But I didn't understand them as they were a little different from my case.
this is the class:
class Segment {
final String chapter;
final String from;
final String to;
Segment({this.chapter, this.from, this.to});
factory Segment.fromJson(Map<String, dynamic> json) {
return Segment(
chapter: json['chapter'],
from: json['from'],
to: json['to'],
);
}
Map<String, dynamic> toJson() {
return {
'chapter': chapter,
'from': from,
'to': to,
};
}
}
these the steps i want to know how to do:
store the string in the object.
Encode the object to JSON.
store it inside sharedprefs.
decode it back and choose a certain item from the list.
You can store a JSON (Map) object with shared preferences in Flutter by encoding the Map to raw JSON (it is basically a String).
To store something with shared preferences you should first of all add it to your dependencies. Here's how.
Then somewhere in your code get the instance of SharedPreferences object by doing:
final prefs = await SharedPreferences.getInstance();
After that you should encode the Map that you got from your Segment class by doing this:
Segment segment = Segment(...);
String rawJson = jsonEncode(segment.toJson());
To save this new JSON with shared preferences run this command to store the whole JSON as a String:
prefs.setString('my_string_key', rawJson);
When you want to read your data from shared preferences use this:
final rawJson = prefs.getString('my_string_key') ?? '';
Map<String, dynamic> map = jsonDecode(rawJson);
final Segment = Segment.fromJson(map);
For more details see this article.

What is the standard for communicating in JSON when you're putting it out and not bringing it in?

I am told I need to communicate in flutter over the web using JSON. I know how to bring in JSON data and convert it into a Dart object. How am I expected to put out the JSON? Should I output the JSON in the form of a Dart object? How does that work? I've tried to do research but can't seem to find the answer.
You can add a method to your class that serialized it into a dictionary like this:
class Car {
final int nWheels;
final String color;
Car(this.nWheels, this.color);
Map<String, dynamic> toMap() => {
"nWheels": this.nWheels,
"color": this.color,
}
}
The resulting map can then be converted to a JSON string by using the flutter json library. That would look like this:
Car car = Car(4, "blue-ish");
String json = jsonEncode(car.toMap());
json is now a JSON-encoded String that can be transmitted to the server.

Parse Json Response through Dictionary object

I have Json response through Facebook API like this:
Now I want to parse this data so I can use within my game. For this purpose, I have written code up to this:
public void OnChallengesButtonClick ()
{
SoundManager.Instance.PlayButtonClickSound ();
FB.API ("/me/friends", HttpMethod.GET, RetrieveFacebookFriends);
}
public void RetrieveFacebookFriends (IGraphResult result)
{
if (!string.IsNullOrEmpty (result.Error)) {
Debug.Log ("ERROR: " + result.Error);
return;
}
IDictionary<string,object> friendsDict = result.ResultDictionary;
Debug.Log ("raw: " + result.RawResult);
}
So how to extract name and ID data from available Json response through Dictionary object?
Basically you should create a class to serialize/deserialize an object:
class FBResponse
FBDataResponse data;
class FBDataResponse
string name;
string id;
Take a look to my response here for more details (after "Final EDIT" part):
Converting arrays of arrays to json
It should be easy to create those two class and use them to deserialize the FB response. Just remember that your variables will need to match the same name with the json response from Facebook (for example you cannot call data data1).
After you have the object it should be easy to do what you want with it... and with the object you should be able to get the json again.
PS. Unity default json serializer does not support Dictionary that's why you need to serialize two object. If you don't want that you should try to use a different library.
I think this is all I need: JSON with Unity
friends = (List<object>)(((Dictionary<string, object>)friendsH) ["data"]);
if(friends.Count > 0) {
var friendDict = ((Dictionary<string,object>)(friends[0]));
var friend = new Dictionary<string, string>();
friend["id"] = (string)friendDict["id"];
friend["first_name"] = (string)friendDict["first_name"];
}
Before I can't able to find this post from Facebook.

Serialization of JSON with unknown key

I'm using the json_serializable pub package for all my RESTful API needs, and it worked great, but now i'm trying to serialize content from Wikipedia.
My problem is with the response structure, one of the keys is unknown beforehand and dynamic according to the returned page ID, here's an example:
I would like to get only the intro part of a page, my query is:
https://en.wikipedia.org/w/api.php?action=query&format=json&prop=extracts&redirects=1&exintro=1&explaintext=1&titles=Stack%20Overflow
The response will be:
{
"batchcomplete": "",
"query": {
"pages": {
"21721040": {
"pageid": 21721040,
"ns": 0,
"title": "Stack Overflow",
"extract": "Stack Overflow is a privately held website, the flagship site of ......."
}
}
}
}
Eventually i would like to get only the internal extract string, but "on my way" there i have the page id under pages.
I can't find how i can get an object when i cannot tell what is the key, neither using json_serializable package nor explicitly writing the JSON import code. Is it possible?
PS, i do think i found a way that required two API calls - if i add the indexpageids flag and set it to true, i will receive an additional dictionary entry called pageids with the page id numbers as string, then i can use the retrieved string in the second API call.
I still don't know the exact way i'll do it but i'm pretty sure it's possible, but sending 2 API requests every time is expensive and i would like to know if there's a more elegant way to do it.
Make sure that you use the latest version of json_serializable. Use a Map<String, Page> for key-value pairs:
#JsonSerializable()
class Query {
final Map<String, Page> pages;
Query(this.pages);
factory Query.fromJson(Map<String, dynamic> json) => _$QueryFromJson(json);
Map<String, dynamic> toJson() => _$QueryToJson(this);
}
Just to prove that it works, here is what json_serializable is generating:
Query _$QueryFromJson(Map<String, dynamic> json) {
return new Query((json['pages'] as Map<String, dynamic>)?.map((k, e) =>
new MapEntry(
k, e == null ? null : new Page.fromJson(e as Map<String, dynamic>))));
}
You can get keys of json & then use that key to fetch value as
var response = await http.get(API_ENDPOINT);
var responseJson = json.decode(response.body);
Map<String, dynamic> json =responseJson['query']['pages'];
String pageId = json.keys.toList()[0]; // 0 for first page, you can iterate for multiple pages
firstPage = json[pageId];
String title = firstPage["title"];
How would you do if you have another level of dynamic keys inside the dynamic key? I had to edit the generated json_serializable for my second class to use json only instead of the generated json['key'].
Page _$PageFromJson(Map<String, dynamic> json) {
return new Page((json as Map<String, dynamic>)?.map((k, e) =>
new MapEntry(
k, e == null ? null : new SecondPage.fromJson(e as Map<String, dynamic>))));
}
It feels like there is some better way? Because this generated code gets overwritten everytime I make changes in any file and run generator..
Im new to SO so I cant rate answers and write comments. But boformer's answer helped me but then I ran into this second question.
Edit: I found the solution by trial and error, used:
class Query {
final Map<String, Map<String, SecondPage>> pages;