【Dart】JSONとモデルの変換コードを自動生成したい

Dart
Dart
この記事は約6分で読めます。

クラスの値を簡単にデバック出力したいと感じたことはないですか?
toStringで、簡単に値を出力したいと感じたことはないですか?

そんな時に、dart:convertjson_serializableを使うと楽ですよ、という話です。
もちろん、JSONのシリアライズとデシリアライズにも使えます。

dart:convertとは

dart:convertは、Base64やJSON、UTF8などを変換するクラスが入ったライブラリです。SDKとして標準パッケージに入っているので、依存関係の設定がなくても、使用できます。

json_serializableとは

json_serializableは、JSONのシリアライズとデシリアライズのコードを生成するライブラリです。
ただ、処理を見ると、モデルとMapの変換をやっているだけのようです。(dart:convertを使うことで、JSONとMapの相互変換ができるので、JSON処理をすることができます)

使い方は、モデルにアノテーションを追加して、コマンドを叩くと、相互変換するコードが生成されます。コードは、変換するだけの関数なので、モデルに関数を追加して使用できるようにすれば、出来上がりです。
コードを生成するためには、3つのパッケージを依存関係に追加する必要があります。

GitHub - google/json_serializable.dart: Generates utilities to aid in serializing to/from JSON.
Generates utilities to aid in serializing to/from JSON. - google/json_serializable.dart
build_runner | Dart package
A build system for Dart code generation and modular compilation.
json_annotation | Dart package
Classes and helper functions that support JSON code generation via the `json_serializable` package.

実装

では、実装していきましょう。
コンソール上で実行していくので、IDEを使っている方は、適宜読み替えてください。

  1. プロジェクト作成
  2. 依存関係の設定
  3. モデルクラスの作成
  4. アノテーションの追加とpartディレクティブの追加
  5. コード生成
  6. 生成されたコードの追加

プロジェクト作成

今回は、Stagehandパッケージを使って、プロジェクトを生成しています。
新規フォルダを作成して、stagehandでシンプルなコンソールアプリの雛形を作ります。

$ mkdir jsontest
$ cd jsontest
$ stagehand console-simple

依存関係の設定

pubspec.yamlに、依存関係を追加します。
開発時にコードを生成をするので、dev_dependenciesに、build_runnerjson_serializableを追加します。
モデルには、アノテーションの設定を行います。シリアライズ、デシリアライズでアノテーションを使用するので、dependenciesに、json_annotationを追加します。
2020年3月10日時点での、最新は以下になります。

$ vi pubspec.yaml
dependencies:
  json_annotation: ^3.0.1

dev_dependencies:
  build_runner: ^1.8.0
  json_serializable: ^3.2.5

依存関係があるパッケージをコマンドでインストールします。

$ pub get

モデルクラスの作成

$ vi bin/example.dart

次のコードをexample.dartにコピーしてください。

import 'package:json_annotation/json_annotation.dart';

part 'example.g.dart'; // コード生成ができていないのでここはエラー

@JsonSerializable(nullable: false)
class Person {
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  Person({this.firstName, this.lastName, this.dateOfBirth});
}

重要なのは、importpart@JsonSerializableです。
part部分は、コード生成後にできるファイルなので、読み込みができなくて、エラーになっているはずです。
また、partのコードがないとコマンド実行で、コード生成がされません。
なお、作られるファイル名も決まっていて、example.dartだったら、example.g.dartのファイルが作成されます。

コード生成

次は、build_runnerによるコード生成です。pubは、パッケージ管理のコマンドです。

$ pub run build_runner build

生成されたコードの追加

クラスに、ファクトリーとtoJson関数を追加してみましょう。また、toStringも追加します。
マッピング自体は、example.g.dartに作成されているはずなので、example.dartでは、それを使います。

$ vi example.dart
import 'package:json_annotation/json_annotation.dart';
import 'dart:convert'; // ここを追加

part 'example.g.dart';

@JsonSerializable()
class Person {
  final String firstName;
  final String lastName;
  final DateTime dateOfBirth;
  Person({this.firstName, this.lastName, this.dateOfBirth});
  factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); // ここを追加
  Map<String, dynamic> toJson() => _$PersonToJson(this); // ここを追加

  @override
  String toString() => json.encode(toJson()); // ここを追加
}

使ってみよう

雛形で、main.dartが作成されているので、それを変更します。
クラスのインスタンスを作成して、toStringの呼び出しを行います。
問題なければ、JSONが出力されます。

$ vi bin/main.dart
import 'example.dart';

void main(List<String> arguments) {
  var p = Person(firstName: 'firstName', lastName: 'lastName', dateOfBirth: DateTime.now());
  print(p.toString());
}
{"firstName":"firstName","lastName":"lastName","dateOfBirth":"2020-03-11T17:26:27.204499"}

コメント