JSON Encode/Decode List with json_serializable - json

I'm using the packages: https://pub.dev/packages/json_annotation, https://pub.dev/packages/json_serializable
I want to encode/decode a list of objects in my flutter app. The class which is in the list is as follows:
#JsonSerializable()
class Trial {
final ProtocolType type;
List<List<double>> data;
#JsonKey()
final DateTime date;
Trial({this.type, this.data, this.date});
Map<String, dynamic> toMap() {
return {'type': this.type, 'data': this.data, 'date': this.date};
}
factory Trial.fromJson(Map<String, dynamic> json) => _$TrialFromJson(json);
Map<String, dynamic> toJson() => _$TrialToJson(this);
}
I want to decode/encode a list of these items, and I couldn't figure out how else to do it so I made a new class:
#JsonSerializable()
class TrialList {
final List<Trial> trials;
TrialList({this.trials});
factory TrialList.fromJson(List json) => _$TrialListFromJson(json);
List toJson() => _$TrialListToJson(this);
}
Notice the List toJson() in this new class. It seems Json_serializable only wants me to decode/encode maps, is there any way to support lists?

Solution:
#JsonSerializable()
class TrialList {
final List<Trial> trials;
TrialList({this.trials});
factory TrialList.fromJson(json) => _$TrialListFromJson({'trials': json});
List toJson() => _$TrialListToJson(this)['trials'];
}
credit to Null from the /r/FlutterDev discord

Related

json serialization with inheritance in dart

I am using json annotation package to generate the json serialization boilerplate code. I have Base class
#JsonSerializable()
class UIWidget {
double? size = 50;
List<UIWidget> children = List.empty(growable: true);
factory UIWidget.fromJson(Map<String, dynamic> json) => _$UIWidgetFromJson(json);
Map<String, dynamic> toJson() => _$UIWidgetToJson(this);
}
I have several subclasses and one such is given below.
#JsonSerializable(explicitToJson: true)
class UIGridView extends UIWidget {
int scrollDirection = Axis.vertical.index;
String _showAxis = "Vertical";
int crossAxisCount = 3;
double mainAxisSpacing = 0.0;
double crossAxisSpacing = 0.0;
double childAspectRatio = 1.0;
factory UIGridView.fromJson(Map<String, dynamic> json) => _$UIGridViewFromJson(json);
Map<String, dynamic> toJson() => _$UIGridViewToJson(this);
}
If you notice, the UIWidget class has children property, which can contain any of the sub classes. The problem arise when I tried to generate the code. The fromJson method is generated as follows
..children = (json['children'] as List<dynamic>)
.map((e) => UIWidget.fromJson(e as Map<String, dynamic>))
.toList();
However, I needed to call the subclass fromjson and create instance of subclass. Is there a way to do this?
Where's your super() constructor? If you're extending UIWidget, you need to pass your params from the extending class to the parent class, and codegen will work as expected.
// Base class
#JsonSerializable()
class ParentClass {
final String? someString;
const ParentClass({
this.someString,
});
factory ParentClass.fromJson(Map<String, dynamic> json) =>
_$ParentClassFromJson(json);
Map<String, String> toJson() => Map.from(_$ParentClassToJson(this));
}
// Child class extending base
#JsonSerializable()
class ChildClass extends ParentClass {
final List<int> exampleList;
const ChildClass({
required this.exampleList,
String? someString,
}) : super(someString: someString);
factory ChildClass.fromJson(Map<String, dynamic> json) =>
_$ChildClassFromJson(json);
Map<String, String> toJson() =>
Map.from(_$ChildClassToJson(this));
}

How Serialize GeoPoint flutter?

flutter pub run build_runner build gives an error on geoPoint. Error says: To support the type GeoPoint you can: Use JsonConverter.
How can I implement it?
class
...
#JsonSerializable()
class Person{
late final String name;
final List<Location> location;
Person(
{required this.uid,
required this.name,
required this.location});
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
Map<String, dynamic> toJson() => _$PersonToJson(this);
}
#JsonSerializable()
class Location {
String name;
GeoPoint geoPoint;
Location({required this.name, required this.geoPoint});
factory Location.fromJson(Map<String, dynamic> json) =>
_$LocationFromJson(json);
Map<String, dynamic> toJson() => _$LocationToJson(this);
}
Example for Colors :
#JsonSerializable()
class Line {
Line({required this.name, required this.color});
factory Line.fromJson(Map<String, dynamic> json) => _$LineFromJson(json);
String name;
#ColorSerializer()
Color color;
Map<String, dynamic> toJson() => _$LineToJson(this);
}
class ColorSerializer implements JsonConverter<Color, int> {
const ColorSerializer();
#override
Color fromJson(int json) => Color(json);
#override
int toJson(Color color) => color.value;
}
Your Code should be something like this:
#JsonSerializable()
class Line {
Line({required this.name, required this.color});
factory Line.fromJson(Map<String, dynamic> json) => _$LineFromJson(json);
String name;
#GeoPointSerializer()
GeoPoint geoPoint;
Map<String, dynamic> toJson() => _$LineToJson(this);
}
class GeoPointSerializer implements JsonConverter<GeoJson, List<int,int>> {
const GeoPointSerializer();
#override
GeoPoint fromJson(List<int,int> json) => GeoPoint(x:json[0],json[1]);
#override
List<int,int> toJson(GeoPoint geoPoint) => [geoPoint.x,geoPoint.y];
}

How to decode nested JSON List of Objects in Dart/Flutter

I'm trying to figure out how I can decode the following JSON in Flutter.
https://covid.ourworldindata.org/data/owid-covid-data.json
I tried the following structure, but it doesn't work.
#JsonSerializable()
class StatisticsResponse {
Map<String, Country> data;
//List<Country> data;
StatisticsResponse({this.data});
factory StatisticsResponse.fromJson(Map<String, dynamic> json) =>
_$StatisticsResponseFromJson(json);
}
#JsonSerializable()
class Country {
String continent;
String location;
int population;
//Map<String, Data> data;
List<Data> data;
Country({this.continent, this.location, this.population, this.data
});
factory Country.fromJson(Map<String, dynamic> json) =>
_$CountryFromJson(json);
}
Use Map to convert dynamic to String. Directly assigning List to List will throw an error. You have create getters yourself.
Consider this example:
(jsonDecode(response.body)["data"] as List).map((e) => e as Map<String, dynamic>)?.toList();
Now assign to an instance of the custom class you have made.

JSON encoding in Dart

I have worked on decoding/encoding JSONs in my Flutter/Dart app. The decoding works just fine, but I have a very nasty problem when encoding my objects to JSON.
These are nested objects. Every one of them has its toJson and fromJson methods, in order to ensure that jsonEncode and Decode works. A small snippet of my work:
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": jsonEncode(users),
};
}
and the nested class:
class User {
int userid;
// actually there's more, including more nested objects
/// JSON-Import
factory User.fromJson(Map<String, dynamic> json) {
return User(
userid: int.parse(json["userid"]),
);
}
/// JSON-Export
Map<String, dynamic> toJson() {
return {
"userid": this.userid,
};
}
}
The problem is: When I encode the top level class "App", it correctly calls the toJson() method of the nested class. However, the corresponding JSON should read like this:
{
"langPref":"de-DE",
"langFallb":"en-GB",
"users":
[
{
"userid": 1
// and so on
It does, however, look like this:
{
"langPref":"de-DE",
"langFallb":"en-GB",
"users":"[{\"userid\":1
// and so on
So, the jsonEncode somehow introduces additional double quotes, which even makes sense somehow. It produces a String, and inside the JSON a string should be encoded .... But I guuess I'm just doing something wrong and missing something obvious .... How can I tell jsonEncode to accept the result of the operation, instead of encoding it as a string?
Can somebody help me?
This problem rises because you use jsonEncode() which return string object
you must use jsonDecode() that return a Map<String, dynamic>
and your App class will be like following
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": jsonEDecode(users),
};
}
Update
2nd method is to remove jsonEncode() without use jsonDecode()
3rd method use tojson() method in user class like following code
"users": users.map((user) => user.tojson()).toList(),
4th method the best method
use json_serializable library with json_annotation library to generate json serialization for annotated classes,
Flutter team approve this method as the best and the official one as described in Official Flutter Documentation.
app.dart
import 'package:json_annotation/json_annotation.dart';
part 'app.g.dart';
#JsonSerializable()
class App{
String langPref;
String langFallb;
List<User> users;
App({this.langPref, this.langFallb, this.users});
factory App.fromJson(Map<String, dynamic> json) => _$AppFromJson(json);
Map<String, dynamic> toJson() => _$AppToJson(this);
}
user.dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
#JsonSerializable()
class User{
int userId;
User({this.userId});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
Great .... I will answer my own question. :-)
Further experiments lead to the following conclusion:
class App {
factory App.fromJson(Map<String, dynamic> json) => App(
langPref: json["langPref"],
langFallb: json["langFallb"],
users: List.of(json["users"]).map((i) => i).toList(),
);
String langPref;
String langFallb;
List<User> users;
/// JSON-Export
Map<String, dynamic> toJson() => {
"langPref": langPref,
"langFallb": langFallb,
"users": users,
};
}
New is only the last line .... I can directly pass the users list. It's not necessary to use jsonEncode for all of the nested objects and lists and maps etc.
Sorry for stealing the time of the readers, but maybe this answer will help others.

How to customize json_serializable library code generation?

I use json_serializable library and firebase for database. I need to serialize and deserialize my data class to/from JSON. But firebase json format looks like this
{
'some_id_here': {
'title': 'some_title',
'content': 'some_content',
'timestamp': 'some_timestamp'
}, ...
}
Here's my class
import 'package:json_annotation/json_annotation.dart';
part 'update.g.dart';
#JsonSerializable()
class Update {
final String id, title, content, timestamp;
Update({this.id, this.title, this.content, this.timestamp});
factory Update.fromJson(Map<String, dynamic> json) => _$UpdateFromJson(json);
Map<String, dynamic> toJson() {
return _$UpdateToJson(this);
}
}
So how to customize the toJson() function so I can deserialize from
Update('ID', 'TITLE', 'CONTENT', 'TIMESTAMP');
to
{
'ID': {
'title': 'TITLE',
'content': 'CONTENT',
'timestamp': 'TIMESTAMP'
}
}
Try this
class ID {
String title;
String content;
String timestamp;
Map<String, dynamic> toJson() {
final Map<String, dynamic> ID = new Map<String, dynamic>();
ID['title'] = this.title;
ID['content'] = this.content;
ID['timestamp'] = this.timestamp;
return ID;
}
}