Tutorials

Snippets

Search

Tags

Subscribe on Youtube

Automatic Json Serializing in Flutter Using Json Annotation

Cover image

I recently wrote a post for beginner on how to parse Json in Flutter. This tutorial covers how to use json_annotation and json_serializable to generate your code for parsing and serializing your models to json.

This way is not always faster than generating your models from your json using a json to dart converter. The places I've seen it work better or reduce work is when you have to write custom logic around how you want to serialize and deserialize your json. Other than that it's not very appealing if you have small models and don't need any custom json parsing rules.

Using the json_annotation is a two step process. You have to annotate your models, and then you have to run

flutter packages pub run build_runner build

To generate the models and the factories for you. This is not a big deal since models don't change often so you don't have to do this all the time. To use this json magic we have to add the following packages:

  • json_serializable: Provides a dart build system that generates code when they find member annotated with classes defined in json_annotation
  • json_annotation: Defines the annotations used by json_serializable
  • build_runner: Provides a way to generate files using Dart code

Only the json_annotation package will be shipped as a dependency, the other two (json_serializable and build_runner) will be dev dependencies. Let's add them to the pubspec.

...
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: ^2.4.0

dev_dependencies:
  flutter_test:
    sdk: flutter
    
  build_runner: ^1.0.0
  json_serializable: ^3.0.0
...

Next we'll create our model. I'll show one exmple and display some of the functionality available to you. Create a file called user.dart with a new class

class User {

}

To indicate this class in serializable we have to annotate it with @JsonSerializable(). One thing to keep in mind is, you're going to be writing code that doesn't exist until the above build command is run. The way this is done is by indicating that this file is part of the generated file, and indicating to the generated file that it's apart of this file. The way we do that is through the part directive. Let's fill the class out by importing json_annotation package, including our partial file and making some basic properties.

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

()
class User {
  final String name;
  final String surname;
  final int age;

  User({this.name, this.surname, this.age});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

Now before you get a fright, the code _$UserFromJson(json) will be generated by the json_serializer when we run build. Let's run the command to get the user.g.dart file. In your project folder run

flutter packages pub run build_runner build

You should see something similar to below, if it's your first time it'll be more text

INFO] Generating build script...
[INFO] Generating build script completed, took 301ms

[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 305ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 1.5s

[INFO] Running build...
[INFO] Running build completed, took 4.7s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 44ms

[INFO] Succeeded after 4.8s with 0 outputs (1 actions)

You'll see a new file created next to your user file named user.g.dart and in there is your serializing code. That's all there is to it. As your model expands and you add new propertiesyou have to regenerate. Lets look at some of the capabilities.

  const JsonKey({
    this.defaultValue,
    this.disallowNullValue,
    this.encodeEmptyCollection,
    this.fromJson,
    this.ignore,
    this.includeIfNull,
    this.name,
    this.nullable,
    this.required,
    this.toJson,
  });

This is the constructor for the JsonKey, as you see there's many options. We'll only look at a few to keep it short.

  (name: 'date-of-birth')
  final DateTime dateOfBirth;

Using the name property you can tell the serializer what the name of the key will be in the json data. This way if you have weird keys like _GMT_time you can still map it to a value in your model without much code.

By using the @JsonKey annotation and passing false to includeIfNull it will be excluded from the json string.

  (includeIfNull: false)
  final String secondName;

You can also supply toJson and fromJson functions that will take in a value and convert it to what you want. The most common application will probably be to take in a Date string and convert it to a DateTime. But we'll do something a bit different. We'll take in a socialID and convert it to a string representing the social platforms name.

  (
    // map from and to the socialId
    name: 'socialId',

    // When deserializing take the socialId value and pass in to _profileLinkFromId
    fromJson: _socialNameFromId,

    // when serializing pass the string value of socialId into _idFromName and use that value
    toJson: _idFromName
  )
  final String profileLink;

  static String _socialNameFromId(int id) {
    if(id == 0) {
      return 'Facebook';
    }

    return 'Reddit';
  }

  static int _idFromName(String socialName) {
    if(socialName == 'FaceBook') {
      return 0;
    }

    return 1;
  }

The JsonSerializable annotation has it's own set of parameters that also allows you to customise your json handling. I won't go over all of them since you can just go to the definition and see all the properties.

Checkout and subscribe to my Youtube Channel for weekly tutorials. Follow me on Instagram for snippets and day-to-day programming. Checkout all the other snippets here. You might find some more Flutter magic.

Also check out

Cover image

How to parse Json in Flutter

This Tutorial shows the best and most convenient way to convert json in Flutter.

Link
Cover image

Flutter Basics - Going from setState to Architecture

In this tutorial I will be going over how to handle a common async situation in Flutter, without throwing architectures at the problem

Link
Cover image

A Guide to setting up better Logging in Flutter

This article covers logging in Flutter to help with debugging.

Link