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);
}
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
};
}
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);
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>
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);
}
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;
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;
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
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);
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;
}
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)