問題
freezedを使う場合に以下のように fromJson()を作ると、Firebaseライブラリを使った場合などでDateTime変換関連のエラーが出てしまいます。
例として、Messageというデータ型を作る場合を考えます。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:meta/meta.dart';
part 'message.freezed.dart';
part 'message.g.dart';
@freezed
abstract class Message with _$Message {
factory Message(
{@required String username,
@required String body,
@required DateTime createdDate}) = _Message;
factory Message.fromJson(Map<String, dynamic> json) =>
_$MessageFromJson(json);
}
freezedによる生成コード
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'message.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_Message _$_$_MessageFromJson(Map json) {
return _$_Message(
username: json['username'] as String,
body: json['body'] as String,
createdDate: json['createdDate'] == null
? null
: DateTime.parse(json['createdDate'] as String),
);
}
Map<String, dynamic> _$_$_MessageToJson(_$_Message instance) =>
<String, dynamic>{
'username': instance.username,
'body': instance.body,
'createdDate': instance.createdDate?.toIso8601String(),
};
自分の場合は、この生成コードのfromJson()中の以下の部分でエラーが出てしまいました。
createdDate: json['createdDate'] == null
? null
: DateTime.parse(json['createdDate'] as String),
);
このコードを以下のように修正することで問題は解決します。
createdDate: json['createdDate'] == null
? null
: json['createdDate'] as DateTime,
);
しかし、生成されたコードを書き換えると、生成されるたびに書き換える必要が出てしまいバグの原因となる可能性があるため、できるだけやりたくありません。
解決策
そんなときは、以下のようにJsonConverterを実装したクラスを作ると、任意の方法でfromJson()を行うことができるようになります。
class MessageConverter implements JsonConverter<Message, Map<String, dynamic>> {
const MessageConverter();
@override
Message fromJson(Map<String, dynamic> json) {
if (json == null) {
return null;
}
// ここで自由に変換方法を指定
return _$_Message(
username: json['username'] as String,
body: json['body'] as String,
createdDate: json['createdDate'] as DateTime,
);
}
@override
Map<String, dynamic> toJson(Message data) => data.toJson();
}
利用する側はこんな感じです。
@freezed
abstract class Message with _$Message {
factory Message(
{@required String username,
@required String body,
@required DateTime createdDate}) = _Message;
// ここでMessageConverterを使う
factory Message.fromJson(Map<String, dynamic> json) =>
messageConverter.fromJson(json);
static const messageConverter = MessageConverter();
}
追記
フィールド毎にもConverterを設定することができるようですので、そちらの方が影響範囲を少なくできてより良いと思います。(以下のコメント参照。 @renoinn さんありがとうございます)
参考