DartでJSONをシリアライズ・デシリアライズする方法は大きく分けて2つあります。
- シリアライズ・デシリアライズ処理を自分で書く
- シリアライズ・デシリアライズ処理のコードを作成してくれるライブラリを使う
この記事は2の手法についての記事です。
今回紹介するjson_serializableの他にもbuilt_valueなどがありますが、この記事では割愛します。
json_serializable
json_serializableはDart公式が提供しているライブラリで、その名の通りJSONのシリアライズ・デシリアライズを助けてくれるものです。
普通にやると自分で書かなければならない面倒で単純なシリアライズ・デシリアライズ部分の処理を、build_runnerを使って自動で生成してくれます。
今回はシリアライズにフォーカスして使い方を解説していきます。
1. モデルクラスをjson_annotationが提供するアノテーションを使って作る
import 'package:json_annotation/json_annotation.dart';
@JsonSerializable(createToJson: false)
class CustomerUser {
final int id;
final String nickname;
final String thumbnail;
CustomerUser({this.id, this.nickname, this.thumbnail});
}
@JsonSerializable
と@JsonKey
があれば大体カバーできます。
2. build_runnerを使って*.g.dart
を作成
build_runnerというライブラリを使います。
このライブラリがcode generationを担います。
run build_runner build
を実行することで、1で作ったクラスに対応するファイルを自動で作ってくれます。
例えば、1で作ったクラスがcustomer_user.dart
に書かれていたとすれば、customer_user.g.dart
を作ってくれます。
$ pub run build_runner build
Flutterの場合は以下です。
$ flutter packages pub run build_runner build
成功すれば以下のようなファイルが作られます。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'customer_user_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
CustomerUser _$CustomerUserFromJson(Map<String, dynamic> json) =>
new CustomerUser(
id: json['id'] as int,
nickname: json['nickname'] as String,
thumbnail: json['thumbnail'] as String);
今回の例はシンプルなモデルクラスなので、作成されるコードもシンプルになります。
1で設定したアノテーションによって内容は変わります。
3. 生成されたコードをモデルの各種値に設定する
生成されたコードは別ファイルにあるので、それらを使用するには以下をモデルクラスがあるファイルの方に追加する必要があります。
part 'customer_user_model.g.dart';
今回の例では_$CustomerUserFromJson
が生成されたのでこれをfactoryコンストラクタに設定します。
factory CustomerUser.fromJson(Map<String, dynamic> json) => _$CustomerUserFromJson(json);
クラス全体は以下になります。
import 'package:json_annotation/json_annotation.dart';
part 'customer_user_model.g.dart';
@JsonSerializable(createToJson: false)
class CustomerUser {
final int id;
final String nickname;
final String thumbnail;
CustomerUser({this.id, this.nickname, this.thumbnail});
factory CustomerUser.fromJson(Map<String, dynamic> json) =>
_$CustomerUserFromJson(json);
}
この程度であれば自分で書いてもさほど変わらないかもですが、複雑になっていけばよりライブラリの便利さがわかると思います(例が微妙ですみません。。)。
おまけTips
JSON keyと変数名が異なる場合
上の例ではJSONのkeyと変数名が同じである前提ですが、異なる場合は多々あるかと思われます。
そう行った場合は@JsonKey
のname
を指定することで解決できます。
@JsonKey(name: 'created_at')
final DateTime createdAt;
パース処理を変えたい場合
例えば、DateTimeとして変数が欲しい場合、普通にDateTimeの変数を定義すると生成されるコードでは以下のようなパース処理になります。
DateTime.parse(json['created_at'] as String)
DateTime.parseではパースできる型が限られています。
パースできない文字列が返って来たらこの処理は失敗してしまうので、処理を変える必要があります。
こういった場合に使えるのが、@JsonKey
のfromJson
です。
これにFunctionを与えるとパース処理がそれに変更されます。
@JsonKey(name: 'year_month', fromJson: _parseYearMonth)
final DateTime yearMonth;
DateTime _parseYearMonth(dynamic value) => DateFormat('yyyy-MM').parse(value);
使ってみた感想
JSONのシリアライズ・デシリアライズにフォーカスしているため、シンプルでわかりやすく学習コストが低いのがいいなと思いました。
他にbuilt_valueも使ってみましたが、json_serializableより複雑かつ面倒だったので個人的にはjson_serializableの方が好きだなと思っています。
ただbuilt_valueの方が多機能ではあるので必ずしもこちらが良い!ということはないです。