プロパティにListオブジェクトを持たせてFirestoreに保存したかったのですが、JSON変換エラーになってしまい、軽くつまずいたので共有します。
最初のコード
@freezed
class ParentHoge with _$ParentHoge {
const factory ParentHoge({
required List<ChildHoge> childHoges,
}) = _ParentHoge;
const ParentHoge._();
factory ParentHoge.fromJson(Map<String, dynamic> json) => _$ParentHogeFromJson(json);
}
エラー内容
Unhandled Exception: Invalid argument: Instance of '_$_ChildHoge'
どうやらPOSTしようとしたけどオブジェクトの部分が、うまくJSONに変換できなかったもよう。
freezedで自動生成されたファイルを見てみると👇のようになってました。
Map<String, dynamic> _$$_ParentHogeToJson(_$_ParentHoge instance) =>
<String, dynamic>{
'childHoges': instance.childHoges
};
解決策
どうやらネストしたクラスがシリアライズされるためには@JsonSerializable
(JSONがネストしていることを示すアノテーション)にexplicitToJson: true
を設定する必要があるらしいです。
json_serializable.dart
ファイルのexplicitToJson
の説明に下記のように記載がありました。
If
true
, generatedtoJson
methods will explicitly calltoJson
on nested objects.
true
の場合、生成されたtoJson
メソッドは、ネストしたオブジェクトに対して明示的にtoJson
を呼び出します。
Afterコード
JsonSerializableで指定してあげたらFirestoreに保存ができるようになりました!
@freezed
class ParentHoge with _$ParentHoge {
@JsonSerializable(explicitToJson: true) // <- これを追加
const factory ParentHoge({
required List<ChildHoge> childHoges,
}) = _ParentHoge;
const ParentHoge._();
factory ParentHoge.fromJson(Map<String, dynamic> json) => _$ParentHogeFromJson(json);
}
freezedで自動生成されたファイルを見てみると、mapでそれぞれJSONに変換してからList化してくれてますね!
Map<String, dynamic> _$$_ParentHogeToJson(_$_ParentHoge instance) =>
<String, dynamic>{
'childHoges': instance.childHoges.map((e) => e.toJson()).toList()
};
おまけ
デフォルトtrueでよくない?って思ったけど過去に同じ疑問を持った方がissueをあげてました。
どうやらあまり使用されていない反面、コードに多くの複雑さを与えてたため、デフォルトtrueにはしなかったみたいですね🤔
その他参考URL