What am I supposed to using this toJson function for? - json

I copied this code from somewhere online that said that it is good to use a model for your data interfacing. I've used the fromJson function a lot so far, but never the toJson function since anytime I need to write data to Firebase the built-in functions let me write in the JSON right then and there. When should I be using this toJson and how would I use it?
ModelFriend.fromJson(Map<dynamic, dynamic>? json): //Transform JSON into model
createDate = json?['createDate'] as String,
modifiedDate = json?['modifiedDate'] as String,
stat = json?['stat'] as String,
uid = json?['uid'] as String,
username = json?['username'] as String;
Map<dynamic, dynamic> toJson() => <dynamic, dynamic>{ //Transforms model into JSON
'createDate': createDate,
'modifiedDate': modifiedDate,
'stat': stat,
'uid': uid,
'username': username,
};

When we want to add documents to Cloud Firestore in Flutter, we can use the following code:
await firestore.doc(documentPath).set(data);
The type of the data variable is Map<String, dynamic>, so you need to convert the model to a map like this:
final Model model = Model(
name,
email,
);
await firestore.doc(documentPath).set(model.toJson());
Also, if you use the code below, you may enter the wrong field and get an error:
await firestore.doc(documentPath).set({
"naem": name, // Typo, "name" becomes "naem"
"email": email,
});
So, the answer is, we need toJson to convert the model to a map and add it to Cloud Firestore.

Related

How do I send data class as json in flutter?

I try to sign up as a user to a back-end that takes some values as String and then a class that is called profile with some Strings within the Profile class. So in short the back-end wants some strings and a class.
Map data = {
'username': name,
'email': email,
'password1': password1,
'password2': password2,
'profile': Profile(countryIsoCode: country),
};
This is how I declare field for backend. And I want to declare countryIsoCode as: 'country_iso_code': country,'
But this is not possible within the Profile. And the backend don't get the value for countryIsoCode but when I did: 'country_iso_code': country,' outside the profile it worked but then the back-end does not get a 'profile' value.
In my class I have also this function, but I don't know is I need it.
Map toJson() => {
'countryIsoCode': countryIsoCode,
};
Then I send body to backend.
String body = jsonEncode(data);
I expect either to make the object to jsonObject or declare 'countryIsoCode': countryIsoCode within Profile.
You can use your the toJson() method on your profile object to convert it to a dictionary that in turn can be converted to a JSON string and sent to the backend.
It will look something like assuming that all the other variables like name, email etc. are strings.
var profile = Profile(countryIsoCode: country);
Map<string, dynamic> dataTransferObject = {
'username': name,
'email': email,
'password1': password1,
'password2': password2,
'profile': profile.toJson(), // Important to call toJson here
};
var json = jsonEncode(dataTransferObject); // jsonEncode from dart::convert

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.

Flutter: how to decode this complex JSON string

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());
}}

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;