DEV Community

Cover image for Starting with Flutter: JSON & Serialization
TheOtherDev/s
TheOtherDev/s

Posted on • Updated on

Starting with Flutter: JSON & Serialization

JSON is a great format to pass data through web and more. Every developer will be facing this dreaded issue: how the **** do I translate a JSON into an object? We are now here to check what Flutter gives us to serialize JSON objects. For the sake of this article we'll consider serializing an "Event" object composed like this:

class Event {
  final String title;
  final String date;
  final String imageUrl;

  Event(this.title, this.date, this.imageUrl);
}
Enter fullscreen mode Exit fullscreen mode

Manual serialization

Manual serialization is natively supported by Dart with the dart:convert library. On our data class we'll need to create 2 functions called fromJson and toJson which, respectevely, will translate a json into our Event and vice versa. fromJson takes a Map<String, dynamic> and will give you an instance of the data class, toJson is a class function which gives a Map starting from data parameters.

class Event {
  final String title;
  final String date;
  final String imageUrl;

  Event (this.title, this.date, this.imageUrl);

  Event .fromJson(Map<String, dynamic> json): 
        title= json['title'],
        date = json['date'],
        imageUrl= json['imageUrl'];

  Map<String, dynamic> toJson() =>
    {
      'title': title,
      'date': date,
      'imageUrl': imageUrl
    };
}
Enter fullscreen mode Exit fullscreen mode

Then serialize and deserialize is an easy line of code:

//JSON String to a map
Map eventMap = jsonDecode(jsonString);

//Serialize to object
var event = Event.fromJson(eventMap);

//Encode to JSON String
String json = jsonEncode(event);
Enter fullscreen mode Exit fullscreen mode

This is easy but be aware that you would probably have also to face more complex objects (like nested JSONs) or you could wish for a more "automatic" way to to this operation.

Code generation libraries

We'll see a very useful package called json_serializable, published by Google, which automatically generates code to serialize and deserialize our data class.

First we need to add our dependencies, we'll need to add also dev_dependencies because there will be code generation from json_serializable that we don't want on our build:

dependencies:
  json_annotation: <latest_version>

dev_dependencies:
  build_runner: <latest_version>
  json_serializable: <latest_version>
Enter fullscreen mode Exit fullscreen mode

Then on our Event class we'll need to say that's a json_serializable class using annotations and allow the class to access private members in generated files:

import 'package:json_annotation/json_annotation.dart';

/// This allows the class to access private members in
/// the generated file called *.g.dart, where the star denotes the source file name.
part 'event.g.dart';

/// An annotation for the code generator to know that this class needs
/// JSON serialization.
@JsonSerializable()

class Event{
  Event(this.title, this.date, this.imageUrl);

  String title;
  String date;
  String imageUrl;

  /// A necessary factory constructor for creating a new Event instance
  /// from a map. Pass the map to the generated `_$EventFromJson()` constructor.
  /// The constructor is named after the source class Event.
  factory Event.fromJson(Map<String, dynamic> json) => _$EventFromJson(json);

  /// `toJson` is the convention for a class to declare support for serialization
  /// to JSON. The implementation simply calls the private, generated
  /// helper method `_$EventToJson`.
  Map<String, dynamic> toJson() => _$EventToJson(this);
}
Enter fullscreen mode Exit fullscreen mode

This will generate code for encoding and decoding all parameters in this class. You can change the name the decoding will find by adding an annotation to the parameter this way:

//On the json it's 'event_image_url'

@JsonKey(name: 'event_image_url')
String imageUrl;
Enter fullscreen mode Exit fullscreen mode

You can also set some defaults, required elements or to ignore some fields:

@JsonKey(defaultValue: 'NoTitle')
String title;

@JsonKey(required: true)
String date;

@JsonKey(ignore: true)
String imageUrl;
Enter fullscreen mode Exit fullscreen mode

Last passage is to run a command to generate JSON serialization code with a watcher that, is some data classes change, also the serialization code will be recreated:

//Run on project root
flutter pub run build_runner watch
Enter fullscreen mode Exit fullscreen mode

in the end we'll just have to call our dear old jsonDecode and fromJson:

Map eventMap = jsonDecode(jsonString);
var event = Event.fromJson(eventMap);
String json = jsonEncode(event);
Enter fullscreen mode Exit fullscreen mode

That's it.

If your class have a nested class you'll need to JsonSerialize also the nested class and on your "father class" you'll need to change this annotation:

@JsonSerializable(explicitToJson: true)
class Event{
  String title;

  //Our class (already json_serialized)
  Address address;
}
Enter fullscreen mode Exit fullscreen mode

That's it. It may seem not the easiest thing in the world but it's better than doing it manually, also if you change something on your object you don't need to change the way the serialization works, you'll just need to redo the command or let the watcher do it. Pretty handy!

Top comments (0)