json_serializable とは
こちら。
APIなどから取得したJSON文字列を、クラスに変換するとき、
final jsonString = '{"id":123, "name":"THE BOOK", "price":1500, "author":{"id":999, "name":"sato"}}';
final jsonStringMap1 = jsonDecode(jsonString);
final author1 = Author(id: jsonStringMap1['author']['id'], name: jsonStringMap1['author']['name']);
final person1 = Book(id: jsonStringMap1['id'], name: jsonStringMap1['name'], author: author1);
print('id: ${person1.id}');
print('name: ${person1.name}');
print('price: ${person1.price}');
print('author_id: ${person1.author?.id}');
print('author_name: ${person1.author?.name}');
のように jsonDecode()
でMap型に変換し、
コンストラクタで、取得したMap型と対象クラスの変数とのマッピングを行い、クラス化する。
この「取得したMap型と対象クラスの変数とのマッピングを行い」部分のコードを自動生成してくれるのが、json_serializable
。
(例として Map型をクラスに変換する部分を扱うが、クラスをMap型に変換する部分のマッピングにも対応している。)
使い方
pubspec.yaml にパッケージを追加
dependencies:
json_annotation: ^4.8.1
dev_dependencies:
build_runner: ^2.4.4
json_serializable: ^6.7.0
json_annotation
のアノテーションを使用して、 build_runner
で生成するので3種類必要。
値を格納するクラスを用意
import 'package:flutter_json_serializable/utils/author.dart';
import 'package:json_annotation/json_annotation.dart';
part 'book.g.dart';
@JsonSerializable()
class Book {
final id;
final name;
final price;
final Author? author;
Book({this.id, this.name, this.price, this.author});
factory Book.fromJson(Map<String, dynamic> json) => _$BookFromJson(json);
Map<String, dynamic> toJson() => _$BookToJson(this);
}
import 'package:json_annotation/json_annotation.dart';
part 'author.g.dart';
@JsonSerializable()
class Author {
final id;
final name;
Author({this.id, this.name});
factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json);
Map<String, dynamic> toJson() => _$AuthorToJson(this);
}
上から説明
-
import 'package:json_annotation/json_annotation.dart';
で json_annotation をインポート -
part '【ファイル名を「.g.dart」拡張子にしたもの】';
を記載して、生成されるファイルと紐付ける
(「ファイル名を「.g.dart」拡張子にした」ファイル名で生成される) -
@JsonSerializable()
で生成対象のクラス、という目印をつける - 必要な変数とコンストラクタを記載
- 基本は変数名とJSONのキー文字列がイコール
-
build.yaml
やJsonKey
を使用して別の文字列とのマッピングにすることもできるが割愛
-
- 型を指定しないとJSON文字列から直接変換しようとするので、Bookクラスのauthorのみちゃんと型(Author)を指定している
- 基本は変数名とJSONのキー文字列がイコール
-
factory 【クラス名】.fromJson(Map<String, dynamic> json) => _$【クラス名】FromJson(json);
を記載することでMap型からクラスへのマッピングメソッドを生成-
【クラス名】.fromJson(【Map型】)
をコンストラクタとして使用できる
-
-
Map<String, dynamic> toJson() => _$【クラス名】ToJson(this);
を記載することでクラスからMap型へのマッピングメソッドを生成 - 今回のように階層が複数ある場合は、階層ごとにマッピング用のクラスが必要となる
生成後のファイルを参照してる部分もあるのでこの時点でエラーが出てるのは気にせず次に進む
コード生成
flutter pub run build_runner build
これを叩くことにより、
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'book.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Book _$BookFromJson(Map<String, dynamic> json) => Book(
id: json['id'],
name: json['name'],
price: json['price'],
author: json['author'] == null
? null
: Author.fromJson(json['author'] as Map<String, dynamic>),
);
Map<String, dynamic> _$BookToJson(Book instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
'price': instance.price,
'author': instance.author,
};
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'author.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Author _$AuthorFromJson(Map<String, dynamic> json) => Author(
id: json['id'],
name: json['name'],
);
Map<String, dynamic> _$AuthorToJson(Author instance) => <String, dynamic>{
'id': instance.id,
'name': instance.name,
};
のようなメソッドが自動生成されるので、
final jsonString2 = '{"id":123, "name":"THE BOOK", "price":1500, "author":{"id":999, "name":"sato"}}';
final person2 = Book.fromJson(jsonDecode(jsonString2));
print('id: ${person2.id}');
print('name: ${person2.name}');
print('price: ${person2.price}');
print('author_id: ${person2.author?.id}');
print('author_name: ${person2.author?.name}');
のように使用できる。