How to Generate Dart code for Parsing JSON in Flutter?

How to Generate Dart code for Parsing JSON in Flutter?

Generated JSON Serialization

Let's check how to do automated JSON Serialization.

Here we will be using random person api for getting the JSON string.
How's it looks like?

Have a look,

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  }
]

First of all, it seems a bit complex right?
But it's not.

Let's say my flutter application need the username and email of the person, that's very easy right?
We can use our manual JSON serialization to get the username and email of the person.
But what if we need the address of the person?
We can also use our manual JSON serialization to get the address of the person.
But it will make our code messy and we don't want that.

Here comes the power of JSON Generated-Serialization in flutter to the rescue.

Please refer to my previous article. This article is in continuation with that article.

Let's Do it.

Step 1:

Add Dependencies in pubsec.yaml file

Add JSON_annotation: ^4.1.0 as dependencies.

JSON_annotation

Step 2:

Add build_runner: ^2.0.6 under dev dependencies.

Add JSON_serializable: ^5.0.0 under dependencies.

build_runner: ^2.0.6
JSON_serializable: ^5.0.0

Dev Dependencies

Here's how my dependencies and dev dependencies look like,

Pubsec.yaml

Step 3:

Create a folder named model inside the lib folder.
It's not necessary but it's good to keep things organized.

Step 4:

Create a file named person_model.dart inside the model folder.
Here we will be using the JSON_serializable package to convert the JSON string into a Person object.


import the JSON_serializable package.

import 'package:JSON_annotation/JSON_annotation.dart';

Create a part of our file.

part 'your_filename.g.dart';

This allows the User class to access private members in the generated file. The value for this is *.g.dart, where the star denotes the source file name.

Step 5:

Now add the following code.

@JsonSerializable()

Hey but what is this?

Basicaly, this annotation tells the Dart compiler to generate a class that can be serialized to JSON.


Step 6:

Create a class named PersonModel.
or what ever you want.
It's not necessary but it's good to keep things organized according to the JSON string.


Now let's look back to our JSON string.

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  }
]

Let's first decide what kind of information we need from the JSON string.

For this example, we need the name, username, email, and address.

Looking at the JSON string, we can see that the address is a JSON object.
Hey but what is a json object?

A json object is a json array with a single element.
for example:

[
  {
    "street": "Kulas Light",
    "suite": "Apt. 556",
    "city": "Gwenborough",
    "zipcode": "92998-3874",
    "geo": {
      "lat": "-37.3159",
      "lng": "81.1496"
    }
  }
]

This is a JSON array with a single element.
And what is a JSON array?

A JSON array is a JSON object with an array as its value.

I think we are clear now.

If not then don't worry, you will get it soon, when you do the code.


Step 7:

We can declare the name, username, and email as strings since they do not contain any nested JSON objects.

here's how our code looks like,

import 'package:json_annotation/json_annotation.dart';

part 'person_model.g.dart';

@JsonSerializable()
class PersonModel {
  String name;
  String username;
  String email;
}

But what about the address?
It's a JSON object with nested JSON objects. Declare a new class named AddressModel. This class will be used to store the address.

Create a new file named address_model.dart, inside our model folder.

And perform the same steps as we did for PersonModel.

here's how our code looks like in the address_model.dart file,

import 'package:json_annotation/json_annotation.dart';

part 'address_model.g.dart';

@JsonSerializable()
class AddressModel {
  String street;
  String suite;
  String city;
  String zipcode;
  // GeoModel geo; // Your homework :)

}

We are not done yet btw.

Both of our programs are incorrect.
Why?

because we haven't initialized our constructors.

How do we initialize our constructors? Or how do we even define a constructor?

But first of all, what is a constructor?

A constructor is a function that is called when an object is created.

You may refer to my previous article #Flutter: A short intro to OOP for more details.

Let's create a constructor for our PersonModel class first.

Add this code inside the PersonModel class,

PersonModel(this.name, this.username, this.email);

And do the same for the AddressModel class.

Add this code inside the AddressModel class,

AddressModel(this.street, this.suite, this.city, this.zipcode);

Hey Gulshan, what is this this.?

It's a reference to the current object.

It is used to access the properties of the current object. i.e. this.name is equivalent to name. I think now it's clear.

Let's Jump back into our person_model.dart file.

Declare a new variable named address of type AddressModel. And do the changes in our constructor accordingly.

Our PersonModel would now look like this,

class PersonModel {
  String name;
  String username;
  String email;
  AddressModel address;
  PersonModel(this.name, this.username, this.email, this.address);
}

Add the following code inside our PersonModel class,

factory PersonModel.fromJson(Map<String, dynamic> json) => _$PersonModelFromJson(json);

Map<String, dynamic> toJson() => _$PersonModelToJson(this);

And add the following code inside our AddressModel class,


@override
String toString() => '${street} ${suite} ${city} ${zipcode}';

factory PersonModel.fromJson(Map<String, dynamic> json) => _$AddressModelFromJson(json);

Map<String, dynamic> toJson() => _$AddressModelToJson(this);

What are those lines of code?

Let's break them down.
What is a factory?

A factory is a function that creates an object.

for now, this much info is enough. You may research more about factories on the web.

what is _$PersonModelFromJson?

It's a private function.
It's used to create an object from a json object.

Why am I seeing an error?

It's because the files are not generated yet.

And at the end that overrides part inside our AddressModel class, is used to just return the string representation of the objects. In our case, street, suite, city, zipcode.

Here's how our code looks like,

person_model.dart

import 'package:json_annotation/json_annotation.dart';
import 'package:your_appname/model/address_model.dart';

part 'person_model.g.dart';

@JsonSerializable()
class PersonModel {
  String name;
  String username;
  String email;
  AddressModel address;
  PersonModel(this.name, this.username, this.email, this.address);

  factory PersonModel.fromJson(Map<String, dynamic> json) => _$PersonModelFromJson(json);
  Map<String, dynamic> toJson() => _$PersonModelToJson(this);

}

address_model.dart

import 'package:json_annotation/json_annotation.dart';

part 'address_model.g.dart';

@JsonSerializable()
class AddressModel {
  String street;
  String suite;
  String city;
  String zipcode;
  // GeoModel geo; // Your homework :)

  AddressModel(this.street, this.suite, this.city, this.zipcode);
  @override
  String toString() => '${street} ${suite} ${city} ${zipcode}';


  factory AddressModel.fromJson(Map<String, dynamic> json) => _$AddressModelFromJson(json);
  Map<String, dynamic> toJson() => _$AddressModelToJson(this);
}

Let's now generate the files.

Open the terminal and type,

flutter pub run build_runner build

And wait for the files to be generated.

Here, you can look that the files are generated and all our errors are gone.

File Structure

Ahh, if you are thinking that why my files appear red?
It's git stuff. It just means that the files are not committed yet.
And the grey files are ignored files.

let's have a look inside our generated files.
Hey look,

generated file

That's what we wrote in our manual Serialization code.

The only thing we are left with is to use this parsed data in our application.

That can be achieved using FutureBuilder. Currently, this article became a little bit more complicated, I feel.

So I will be posting about FutureBuilder in a future article.


If you are frustrated about how these classes are getting the JSON string, then please refer to my previous article on manual Serialization.


Until then you can refer to Flutter docs for more details.
Or you can also wait for me to write a blog post about this.

Maybe when you are done with this article, I already posted an article about the FutureBuilder.

So have a look at my site.

Thanks for reading.
Happy Coding :)

Did you find this article valuable?

Support Gulshan Yadav by becoming a sponsor. Any amount is appreciated!