Mobile Development 23 min read

Best Practices for JSON Serialization and Deserialization in Flutter Using json_serializable

This article explains the fundamentals of JSON serialization and deserialization in Flutter, compares manual and automated approaches, introduces the json_serializable library and its annotations, demonstrates generic handling, custom converters, and tooling to streamline model generation for robust mobile app development.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Best Practices for JSON Serialization and Deserialization in Flutter Using json_serializable

1. Introduction

In a previous article the author noted that the JSON serialization/deserialization and network request sections were simplistic and not suitable for real projects, so this section presents the author’s best practices for handling JSON in Flutter, inviting readers to share alternative methods.

QuickType + json_serializable can generate model classes from backend JSON, use generics to reduce duplicate models, configure build.yaml to avoid repetitive property settings, and customize field serialization as needed.

2. Basics

2.1 JSON Serialization vs. Deserialization

Serialization : Object → JSON string, used when sending a request.

Deserialization : JSON string → Object, used when parsing a response.

2.2 Flutter Support for JSON

Flutter provides the dart:convert library with jsonEncode and jsonDecode methods. A simple example:

import 'dart:convert';
void main() {
  String jsonString = '{"name": "Jay", "age": 30}';
  // Decode (deserialization)
  Map
userMap = jsonDecode(jsonString);
  print(userMap); // {name: Jay, age: 30}
  // Encode (serialization)
  Map
userMap2 = {'name': 'Jay', 'age': 30};
  String jsonString2 = jsonEncode(userMap2);
  print(jsonString2); // {"name":"Jay","age":30}
}

In practice developers create model classes with fromJson() and toJson() methods, typically named as such by convention.

When json.encode() is called, Dart automatically looks for a toJson() method on the object, as defined by JsonEncoder .

2.3 Why JSON Handling in Flutter Can Be Cumbersome

Manual field mapping leads to repetitive work and potential errors. Unlike Java’s runtime reflection (Gson/Jackson), Flutter disables reflection to preserve tree‑shaking, so code generation is the recommended solution.

The official recommendation is to use the json_serializable code‑generation tool.

3. json_serializable Library

The library solves automatic generation of JSON (de)serialization code.

3.1 Adding Dependencies

json_annotation – defines annotations.

json_serializable – generates code based on annotations.

build_runner – runs the code‑generation tasks.

Two ways to add them:

# Method 1: command line
flutter pub add json_annotation dev:build_runner dev:json_serializable

# Method 2: edit pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  json_annotation: ^4.8.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.4.7
  json_serializable: ^6.7.1

3.2 Basic Usage

Annotate a model class with @JsonSerializable() and add fromJson() and toJson() factories:

import 'package:json_annotation/json_annotation.dart';

part 'banner.g.dart';

@JsonSerializable()
class Banner {
  @JsonKey(name: 'id')
  final int bid;
  final String desc;
  final String imagePath;
  final int isVisible;
  final int order;
  final String title;
  final int type;
  final String url;

  Banner({required this.bid, required this.desc, required this.imagePath, required this.isVisible, required this.order, required this.title, required this.type, required this.url});

  factory Banner.fromJson(Map
json) => _$BannerFromJson(json);
  Map
toJson() => _$BannerToJson(this);
}

Run the generator:

flutter pub run build_runner build --delete-conflicting-outputs

The command creates a banner.g.dart file with the generated serialization logic.

3.3 Advanced Usage

3.3.1 Generics

Many APIs return a wrapper object with data , errorCode , and errorMsg . Using generic response classes reduces duplication.

@JsonSerializable(genericArgumentFactories: true)
class DataResponse
{
  final T? data;
  final int errorCode;
  final String errorMsg;

  DataResponse({required this.data, required this.errorCode, required this.errorMsg});

  factory DataResponse.fromJson(Map
json, T Function(dynamic) fromJsonT) => _$DataResponseFromJson(json, fromJsonT);
  Map
toJson(dynamic Function(T) toJsonT) => _$DataResponseToJson(this, toJsonT);
}

@JsonSerializable(genericArgumentFactories: true)
class ListResponse
{
  final List
? data;
  final int errorCode;
  final String errorMsg;

  ListResponse({required this.data, required this.errorCode, required this.errorMsg});

  factory ListResponse.fromJson(Map
json, T Function(dynamic) fromJsonT) => _$ListResponseFromJson(json, fromJsonT);
  Map
toJson(dynamic Function(T) toJsonT) => _$ListResponseToJson(this, toJsonT);
}

Example usage with a User model demonstrates deserialization of both single objects and lists.

3.3.2 Reducing Repetitive Settings with build.yaml

Configure global options such as explicit_to_json: true and include_if_null: false to avoid adding the same annotation parameters to every model.

targets:
  $default:
    builders:
      json_serializable:
        options:
          explicit_to_json: true
          include_if_null: false

3.3.3 Custom Field (De)Serialization

Use @JsonKey with fromJson and toJson to handle special cases, such as converting a timestamp string to DateTime and back.

@JsonSerializable()
class User {
  String name;
  @JsonKey(fromJson: _timeStampFromJson, toJson: _timeStampToJson)
  DateTime timestamp;

  User({required this.name, required this.timestamp});

  factory User.fromJson(Map
json) => _$UserFromJson(json);
  Map
toJson() => _$UserToJson(this);

  static DateTime _timeStampFromJson(String ts) => DateTime.fromMillisecondsSinceEpoch(int.parse(ts));
  static String _timeStampToJson(DateTime dt) => dt.millisecondsSinceEpoch.toString();
}

3.3.4 Custom Converters (JsonConverter)

Define a JsonConverter to map JSON values to Dart enums or other types.

enum DayOfWeek { monday, tuesday, wednesday, thursday, friday, saturday, sunday }

class DayOfWeekConverter implements JsonConverter
{
  const DayOfWeekConverter();
  @override
  DayOfWeek fromJson(String json) {
    switch (json.toLowerCase()) {
      case 'monday': return DayOfWeek.monday;
      case 'tuesday': return DayOfWeek.tuesday;
      // ... other cases ...
      default: throw ArgumentError('Invalid day: $json');
    }
  }
  @override
  String toJson(DayOfWeek obj) => obj.toString().split('.').last;
}

@JsonSerializable()
class MyModel {
  @DayOfWeekConverter()
  DayOfWeek dayOfWeek;
  MyModel(this.dayOfWeek);
  factory MyModel.fromJson(Map
json) => _$MyModelFromJson(json);
  Map
toJson() => _$MyModelToJson(this);
}

3.4 Common Questions

3.4.1 Should .g.dart files be committed?

There is no strict rule; teams may choose to commit them for convenience or ignore them to avoid merge conflicts, since they are generated.

3.4.2 Automating Code Generation

Running flutter pub run build_runner after each model change can be tedious. Use the watch mode:

flutter packages pub run build_runner watch

IDE integrations (e.g., VS Code tasks) can bind this command to a shortcut for faster workflow.

4. Tools for Generating Model Classes from JSON

Besides json_serializable , several online and IDE tools can generate Dart models:

QuickType – copy JSON and get Dart code with optional json_serializable annotations.

json2dart – similar web‑based generator.

Android Studio plugins such as JsonToDart or FlutterJsonBeanFactory.

CLI tools like fluttercandies/JsonToDart and flutterchina/json_model .

These tools speed up model creation, after which developers may fine‑tune class names, add comments, or write custom scripts for bulk generation.

DartFluttercode generationSerializationJSONdeserializationjson_serializable
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.