JSON
- What is JSON?
- Serializing JSON inside model classes
- Serializing JSON inline
JSON, which stands for JavaScript Object Notation, is an open-standard format used on the web and in mobile clients. It’s the most widely used format for Representational State Transfer (REST)-based APIs that servers provide. If you talk to a server that has a REST API, it will most likely return data in a JSON format. An example of a JSON response looks something like this:
"title": "The Shawshank Redemption",
"rank": "1",
"id": "tt0111161"
}
That is an example movie response that contains three fields.
While it’s possible to treat the JSON as just a long string and try to parse out the data, it’s much easier to use a package that already knows how to do that. Flutter has a built-in package for decoding JSON, but in this chapter, you’ll use the json_serializable and json_annotation packages to help make the process easier.
Flutter’s built-in dart:convert package contains methods like json.decode and json.encode, which converts a JSON string to a Map<String, dynamic> and back. While this is a step ahead of manually parsing JSON, you’d still have to write extra code that takes that map and puts the values into a new class.
The json_serializable package comes in handy because it can generate model classes for you according to the annotations you provide via json_annotation. Before taking a look at automated serialization, you’ll see in the next section what manual serialization entails.
To convert the JSON above to a model class, you’d first create a Movie model class:
class Movie {
final String title;
final int rank;
final String id;
Movie(this.title, this.rank, this.id);
}
Then you’d add toJson() and fromJson() factory methods:
class Movie {
final String title;
final int rank;
final String id;
Movie(this.title, this.rank, this.id);
factory Movie.fromJson(Map<String, dynamic> json) {
return Movie(
json['title'] as String, json['rank'] as int, json['id'] as String);
}
Map<String, dynamic> toJson() {
return <String, dynamic>{'title': title, 'rank': rank, 'id': id};
}
}
In fromJson(), you grab data from the JSON map variable named json and convert
it to arguments you pass to the Movie constructor. In toJson(), you construct a
map using the JSON field names.
By looking at the, dart:convert documentation, you’ll see that you can decode the JSON by calling the jsonDecode() function, with the JSON string as the method argument.
import 'dart:convert';
void main() {
String rawJson = '{"name":"Mary","age":30}';
Map<String, dynamic> map = jsonDecode(rawJson);
print(map);
}
Unfortunately, jsonDecode() returns a Map<String, dynamic>, meaning that you do not know the types of the values until runtime. With this approach, you lose most of the statically typed language features: type safety, autocompletion and most importantly, compile-time exceptions. Your code will become instantly more error-prone. For example, whenever you access the name or email fields, you could quickly introduce a typo. A typo that the compiler doesn’t know about since the JSON lives in a map structure.