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_annotationbuild_runner and json_serializable in the pupspec.yamlfile.

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.

json_serialization_generated_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…

Recommended Training – Treehouse

TreehouseFrom beginner to advanced, our recommended coding training is Treehouse.

Treehouse is an online training service that teaches web design, web development and app development with videos, quizzes and interactive coding exercises.

Treehouse's mission is to bring technology education to those who can't get it, and is committed to helping its students find jobs. If you're looking to turn coding into your career, you should consider Treehouse.

 

Disclosure of Material Connection: Some of the links in the post above are “affiliate links.” This means if you click on the link and purchase the item, we will receive an affiliate commission. Regardless, we only recommend products or services we use personally and believe will add value to our readers.

Author

I’m a mobile product devsigner (i.e. I consider myself as both a developer and a designer) and user experience/interface engineer. I’m an expert on the Android platform and have been recognized as it by the community.

Write A Comment