JSON Serialization in Flutter

JSON serialization in Flutter

Manually serialize JSON with dart:convert

Serialize JSON inline
String json = "{\"name\": \"John Smith\",\"email\":\"[email protected]\"}";
//Parse the string and return the resulting Json object
var userObject = jsonDecode(json);
print("name=${user['name']}");
  • 1
  • 2
  • 3
  • 4

jsonDecode doesn't know the type of the value until runtime. With this approach, we lose most of the statically typed language features: type safety, autocompletion, and most importantly, compile-time exceptions. As a result, our code can become very error-prone.

For example, when we visitnameoremailWhen we entered the field, we typed it very quickly, resulting in the wrong field name. But since this JSON is in the map structure, the compiler doesn't know the wrong field name

Serialize JSON in model class

Introduce a simple model class to solve the aforementioned problems

class User {
  final String name;
  final String email;

  User(this.name, this.email);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'];

  Map<String, dynamic> toJson() => {
        'name': name,
        'email': email,
      };
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

use:

  var userData = User.fromJson (userObject);
  print("name = ${userData.name}");
  • 1
  • 2

Serialize JSON using code generation library

Use json_serializable to generate code to serialize json
Set json_serializable in the project

pubspec.yaml

dependencies:
  json_serializable: ^4.1.0
dev_dependencies:
  build_runner: ^1.0.0
  • 1
  • 2
  • 3
  • 4

Run flutter pub get in your project root folder (or hit "Pub get" in the editor)

Create model class in json_serializable way
import 'package:json_annotation/json_annotation.dart';
//Write manually, user.g.dart will be generated automatically after we run the generate command
part 'user.g.dart';

@JsonSerializable ()
class User {
  final String name;
  final String email;
  final DateTime? dateOfBirth;
  User(this.name, this.email, this.dateOfBirth);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

It is also easy to customize the naming strategy if needed. For example, if we are using an API that returns objects with _snake_case_, but we want to use _lowerCamelCase_ in our model, then we can use the @JsonKey annotation:

/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: 'registration_date_millis')
final int registrationDateMillis;
  • 1
  • 2
  • 3
  • 4

At this time, it was found that _$UserFromJson(json) and _ $UserToJson(this) were red . In order to solve this problem, we must run the code generator to generate serialization templates for us

Run the code generator

There are two ways to run the code generator:

one-time generation

Run the following command in your project directory

flutter pub run build_runner build
  • 1

We can generate json serialization code for our model when needed. This triggers a one-time build, which goes through our source files, picks the relevant ones and generates the necessary serialization code for them

Continuous generation

Run the following command in your project directory

flutter pub run build_runner watch
  • 1

Using _watcher_ can make the process of generating our source code more convenient. It monitors changes to files in our project and automatically builds the necessary files when needed

Generated code in part 'user.g.dart':

part of 'user.dart';

//************************************************ ****************************
// JsonSerializableGenerator
//************************************************ ****************************

User _$UserFromJson(Map<String, dynamic> json) {
  return User(
    json['name'] as String,
    json['email'] as String,
    json['dateOfBirth'] == null
        ? null
        : DateTime.parse(json['dateOfBirth'] as String),
  );
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'email': instance.email,
      'dateOfBirth': instance.dateOfBirth?.toIso8601String(),
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

Use the same as above:

    var userObject = jsonDecode(json);
    var userData = User.fromJson (userObject);
    print("name = ${userData.name}");
  • 1
  • 2
  • 3

Reference: JSON and Serialization

​json processing

Related: JSON Serialization in Flutter