Help us understand the problem. What is going on with this article?

JSON Serialization in Dart ~json_serializableの巻~

More than 1 year has passed since last update.

DartでJSONをシリアライズ・デシリアライズする方法は大きく分けて2つあります。

  1. シリアライズ・デシリアライズ処理を自分で書く
  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と変数名が同じである前提ですが、異なる場合は多々あるかと思われます。
そう行った場合は@JsonKeynameを指定することで解決できます。

@JsonKey(name: 'created_at')
final DateTime createdAt;

パース処理を変えたい場合

例えば、DateTimeとして変数が欲しい場合、普通にDateTimeの変数を定義すると生成されるコードでは以下のようなパース処理になります。

DateTime.parse(json['created_at'] as String)

DateTime.parseではパースできる型が限られています。
パースできない文字列が返って来たらこの処理は失敗してしまうので、処理を変える必要があります。
こういった場合に使えるのが、@JsonKeyfromJsonです。
これに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の方が多機能ではあるので必ずしもこちらが良い!ということはないです。

Further references

kitoko552
Software Engineer at CyberAgent, Inc. Flutter/Dart, iOS/Swift, Node.js, Kubernetes
https://note.com/kitoko552
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away