Auto Generated Json Deserialization With Json Annotation In Flutter
If you’re coming from an Android background like me then you’ve probably missed those POJO classes in Flutter. I believe the developers who do app development in flutter will encounter such problems. After requesting data from the server, the server will often return a json string and if we want to use data flexibly, we need to convert the json string into an object
.
Since flutter only provides json to Map
. Handwritten deserialization is extremely unstable in large projects and can easily lead to parsing failure. So today I will introduce you to json_annotation, an automatic deserialization library recommended by flutter team.
What you’ll learn
- Generate code with
build_runner
. - How to parse json object in the flutter with
json_serialization
.
Include Dependencies
We’re gonna need to add some libraries in the pupspec.yaml
, which is the package management and build system file. Here we need to add three dependencies json_annotation
, build_runner
and json_serializable
in the pupspec.yaml
file.
dependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 json_annotation: ^1.2.0 // dependecy dev_dependencies: flutter_test: sdk: flutter build_runner: ^1.0.0 // | // | -> dev dependencies json_serializable: ^1.5.1 // |
Once you have done these run packets get which is in the toolbar of the file from IntelliJ/Android Studio you can also execute flutter packages pub get
from the terminal in the current project directory if you prefer that.
Now let’s say we need to make a login request and download some json content. The following shows the sample json of a simplified login rest call.
{ "status" : true, "message" : "User successfuly logged in!", "user_name" : "Ahsen Saeed", "profile_url" : "www.codinginfinite.com", "user_id" : 280 }
Now we need to write a dart entity class based on above json data.
class LoginResponse{ bool status; String message; String userName; String profileUrl; int userId; LoginResponse(this.status,this.message,this.userName,this.profileUrl,this.userId); factory LoginResponse.fromJson(map<String,dynamic> json) { return LoginResponse( status : json['status'], message : json['message'], userName : json['user_name'], profileUrl : json['profile_url'], userId : json['user_id'] ) } }
I know, I know, I just want to show you guys, the manual deserialization before showing the auto-generated json deserialization.
Generate an auto-generated json File
The following shows the JsonSerializer
model of above json.
import 'package:json_annotation/json_annotation.dart'; @JsonSerializable() class LoginResponse { bool status; String message; @JsonKey(name: 'user_name') String userName; @JsonKey(name: 'profile_url') String profileUrl; @JsonKey(name: 'user_id') int userId; LoginResponse( this.status, this.message, this.userName, this.profileUrl, this.userId); }
If we want to use JsonSerializer
to generate code, we must add the annotation @JsonSerializable()
before the signature of class that needs to generate the code and if you need to define the name case of the member, use the @JsonKey
annotation.
So, the question is how should the code be generated..? If you guys have remembered that we add the build_runner
dependency in our pupspec.yaml
file.
So, in order to generate the Pojo class for a LoginResponse
run the following command in the current project directory
flutter packages pub run build_runner build
After the command runs successfully, we should be able to find a new file under the entity file.
The models.g.dart json parsing file generated by build_runner
based on JsonSerializer
. Below is the generated dart file.
// ************************************************************************** // JsonSerializableGenerator // ************************************************************************** LoginResponse _$LoginResponseFromJson(Map<String, dynamic> json) { return LoginResponse( json['status'] as bool, json['message'] as String, json['user_name'] as String, json['profile_url'] as String, json['user_id'] as int); } Map<String, dynamic> _$LoginResponseToJson(LoginResponse instance) => <String, dynamic>{ 'status': instance.status, 'message': instance.message, 'user_name': instance.userName, 'profile_url': instance.profileUrl, 'user_id': instance.userId };
Now we only need to associate our generated file in our entity class and provide a way to parse the json in the entity class. Let’s see how we can associate the generated file with our generated file.
import 'package:json_annotation/json_annotation.dart'; part 'package:flutter_projects/models/models.g.dart'; // associated generated dart file @JsonSerializable() class LoginResponse { bool status; String message; @JsonKey(name: 'user_name') String userName; @JsonKey(name: 'profile_url') String profileUrl; @JsonKey(name: 'user_id') int userId; LoginResponse(this.status, this.message, this.userName, this.profileUrl, this.userId); factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json); }
In order for the entity class file to find the generated file, we need part
and let the entity class to mix with the generated file. Finally, a factory constructor is provided, which actually calls _$LoginResponseFromJson
the method of the generated file. The _$LoginResponseFromJson
method is the one who deserializes our json. And that’s how we can simply deserialize our json into dart object.
Let’s take another example where we have a user json and within that user object, we have subjects of a user. Let’s see the json first.
{ "status" : true, "message" : "User successfuly logged in!", "user_name" : "Ahsen Saeed", "profile_url" : "www.codinginfinite.com", "user_id" : 280, "subjects" : [ { "subject_name" : "ComputerProgramming" }, { "subject_name" : "Calculus" } ] }
You see in order to parse the above json, we need to add a list of subjects in our LoginResponse
model.
@JsonSerializable() class LoginResponse { bool status; String message; @JsonKey(name: 'user_name') String userName; @JsonKey(name: 'profile_url') String profileUrl; @JsonKey(name: 'user_id') int userId; @JsonKey(name: 'subjects') List<Subject> subjects; LoginResponse(this.status, this.message, this.userName, this.profileUrl, this.userId, this.subjects); factory LoginResponse.fromJson(Map<String, dynamic> json) => _$LoginResponseFromJson(json); } @JsonSerializable() class Subject { @JsonKey(name: 'subject_name') String subjectName; Subject(this.subjectName); factory Subject.fromJson(Map<String, dynamic> json) => _$SubjectFromJson(json); }
Finally, below is the newly generated file.
// ************************************************************************** // JsonSerializableGenerator // ************************************************************************** LoginResponse _$LoginResponseFromJson(Map<String, dynamic> json) { return LoginResponse( json['status'] as bool, json['message'] as String, json['user_name'] as String, json['profile_url'] as String, json['user_id'] as int, (json['subjects'] as List) ?.map((e) => e == null ? null : Subject.fromJson(e as Map<String, dynamic>)) ?.toList()); } Map<String, dynamic> _$LoginResponseToJson(LoginResponse instance) => <String, dynamic>{ 'status': instance.status, 'message': instance.message, 'user_name': instance.userName, 'profile_url': instance.profileUrl, 'user_id': instance.userId, 'subjects': instance.subjects }; Subject _$SubjectFromJson(Map<String, dynamic> json) { return Subject(json['subject_name'] as String); } Map<String, dynamic> _$SubjectToJson(Subject instance) => <String, dynamic>{'subject_name': instance.subjectName};
I hope this article, gives you a good understanding of how to deserialize the json with json_annotation library into plain old dart object. If you’ve enjoyed this story, share this article with flutter community.
Thank you for being here and keep reading…