19
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

freezedやjson_serializableのfromJson()でDateTimeの変換がうまくいかないときの解決法

Last updated at Posted at 2020-10-23

問題

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 さんありがとうございます)

参考

僕のTwitter

https://twitter.com/tetsufe_

19
6
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?