概要
どうも、@daiki1003です!アプリを作っていく上で、通信の結果をjsonでシリアライズ/デシリアライズすると言う用件は
かなり出てくると思います。
そんな中で選択肢として出てくるのは json_serializable
と freezed
。
この記事は
・この2つのうちどちらを最終的に選ぶべきかと言う悩みを持っている方
・freezedの強みって一体なんやねんと言う思いを持っている方
に向けた記事になるかなと思います。
僕は最初、json_serializable
で行っていましたが
ある時にfreezed
にして完全にこっちでええやんとなりました。
サマリ
json_serializable
よりfreezed
が優れていると思った点をざっくりと。
(freezed
の絶対的価値と言うよりは、json_serializable
と比較して)
・変数名の記述を2度しなくて良い
・fromJson
のみの記述で良い (toJson
は自動生成)
・toString
も自動生成 (これ地味に良い)
・copyWith
が自動で生えてくれる
・(当たり前っちゃ当たり前だが)getterのみしか生えない
それでは解説に行きます。
json_serializableでまずはやってみる
基本的な使い方は以下。
import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'person.g.dart'; @JsonSerializable( nullable: false, fieldRename: FieldRename.snake ) class Person { const Person({ required this.id, required this.phoneNumber, }); factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); Map< String, dynamic> toJson() => _$PersonToJson(this); final String id; final String phoneNumber; Person copyWith({String? id, String? phoneNumber}) { return Person( id: id ?? this.id, phoneNumber: phoneNumber ?? this.phoneNumber, ); } @override String toString() { return 'Person(id: $id, phoneNumber: $phoneNumber); } }
これでbuild_runner
を実行すると以下の様なファイルができます。
// person.g.dart part of 'person.dart'; Person _$PersonFromJson(Map<String, dynamic> json) { return Person( id: json['id'] as String, phoneNumber: json['phone_number'] as int, ); } Map<String, dynamic> _$PersonToJson(Person instance) => <String, dynamic>{ 'id': instance.id, 'phone_number': instance.phoneNumber, };
json_serializable
はjsonとのやりとり部分のみの上記ファイルを自動生成してくれます。
次にfreezedでやってみる
少し語弊はあるかもですが、freezed
はjson_serializable
の拡張版と考えていただいても良いと思います。
import 'package:flutter/foundation.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'person.freezed.dart'; part 'person.g.dart'; @freezed abstract class Person with _$Person { const factory Person({ required String id, required String phoneNumber, }) = _Person; factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json); }
json_serializable
と比較すると、少し記述に関しては癖が強いですが
記述量は明らかに減っています。
特にメンバ変数が増えてくると一層その差を強く感じることができます。
これを記述して、同じ様にbuild_runner
を走らせると様々なメソッドが実装されたperson.freezed.dart
とperson.g.dart
が生成されます。
json_serializableと比較してfreezedが優れていると思ういくつかの理由
変数名の記述を2度しなくて良い
上記を比較してもらうと、json_serializable
は
コンストラクタと変数宣言のため2度変数名を記述しています。
上記の例くらいであればそんなに問題はないですが
10個とか20個とか変数があるとこれが結構辛いです。
なお、freezed
の例ではrequired
としていますが
デフォルト値も以下の様に記述する事が出来ます。
const factory Person({ @Default('') String id, @Default('000-0000-0000') String phoneNumber, }) = _Person;
その際、build.yaml
などでjson_serializable
のオプション値にnullable: false
を指定していると失敗しますので注意が必要です。
fromJsonのみの記述で良い (toJsonは自動生成)
json_serializable
は、言ってみれば「jsonとのやり取り部分は作るからあとはよろしく。」
と言う様なもんです。
なので、fromJson/toJson
の宣言、実装をこちら側でする必要があります。
それに対してfreezed
はそのjsonとのやりとり部分まで一部自動化してくれます。
※とは言え、この辺はファイルテンプレートなりを作っていればそこまで大きな利点とはなりにくいですが。
toStringも自動生成 (これ地味に良い)
これはね、地味に最高です。
デバッグの際にちゃんとパース出来ているかを確認するために
print(person);
とやっても
Instance of 'Person'
としか、出てこず肝心の中身がみれません。
基本的にprintにカスタムクラスのインスタンスが渡されると、toString
がoverrideされていなければ、上記の様にクラス名を表示するだけになっています。
変数の中身などをログ出力したい場合は、上記の例の様にtoString
をoverrideし、各メンバ変数を表示する様に実装しなければなりません。
これ結構大変なんです。。。
copyWithが自動で生えてくれる
この記事では詳細は省きますが、モデルクラスはimmutable
が推奨されています。
となると、何か一つのメンバ変数を変更したい場合はその部分だけ変更した
インスタンスを作り直すことになります。
その際に使うのがcopyWith
メソッドです。
引数は全てoptional
で、そのクラス自身が持つメンバ変数を基本全て受け取る事が出来る様にし、
渡されたものだけ上書きして新しいインスタンスを返すメソッドです。
Person copyWith({String id, String phoneNumber}) { return Person( id: id ?? this.id, phoneNumber: phoneNumber ?? this.phoneNumber, ); }
これも変数が増えてくると地味に記述が大変なのですが、freezed
であれば自動生成してくれます。
(当たり前っちゃ当たり前だが)getterのみしか生えない
上記の話とリンクはしてくるのですが、immutable
にするためには
外部から変数を設定する事が出来てはいけません。
そのため、メンバ変数は読み込み専用にすべきですね。json_serializable
の例だと変数名に_がついておらず、setterが禁止されていないので
外部クラスから値を変更できてしまいます。
その他
いかがでしたでしょうか?json_serializable
に比べて、freezed
の利点を理解していただけたでしょうか?
自分のプロジェクトでは結構な数をjson_serializable
で進めてしまったので
粛々とfreezed
に変更して行きたいと思います笑
Twitterフォローお願いします
「次回以降も記事を読んでみたい!」「この辺分からなかったから質問したい!」
そんな時は、是非Twitter (@daiki1003)やInstagram (@ashdik_flutter)のフォローお願いします♪
Twitterコミュニティ参加お願いします
Twitterコミュニティ「Flutter lovers」を開設しました!参加お待ちしております😁
☕️ Buy me a coffee
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!
コメント
[…] 【Flutter】json_serializableじゃなく、やっぱりfreezedが正解だった話New […]