LoginSignup
14
11

More than 1 year has passed since last update.

【Flutter】【Freezed】JsonConverterを使った独自のカスタムコンバータを作る

Posted at

今回は、JsonConverterを使用して、Freezedで使えるカスタムコンバータを作成する方法を書いていきます。

初めに

例えばこんなFreezedのモデルクラスがあるとします

@freezed
abstract class Profile with _$Profile {
  const factory Profile({
   String? name,
   String? city,
  }) = _Profile;

  factory Profile.fromJson(Map<String, dynamic> json) => _$ProfileFromJson(json);
}

Jsonデータをパースするために fromJson() を用意しています

この時 cityには "tokyo", "osaka", "fukuoka" といった特定の値が入るとします。

こんな時 city を文字列ではなく Enum で持ちたいと思ったことはありませんか?

単にcityの型をEnumにするだけだとJsonからパースができません。

enum City {
  tokyo,
  osaka,
  fukuoka,
}

@freezed
abstract class Profile with _$Profile {
  const factory Profile({
    String? name,
    City? city, // ←パースができない
  }) = _Profile;

  factory Profile.fromJson(Map<String, dynamic> json) => _$ProfileFromJson(json);
}

そんな時に使えるのがJsonConverter です

JsonConverterとは

JsonConverterとは、JsonConverterをインターフェースとしたクラスを作成することで、独自の fromJsontoJsonを実装することができます。
実際に作ってみていきましょう

JsonConverterを実装したカスタムコンバーター

今回のケースでは Enum → String String → Enum といった変換を行いたいので enum_to_string というパッケージを使用します

実際にJsonConverterで実装したものがこちら

enum City {
  tokyo,
  osaka,
  fukuoka,
}

class CityEnumConverter implements JsonConverter<City?, String?> {
  const EnumConverter();

  @override
  City? fromJson(String? json) => EnumToString.fromString(
        City.values,
        json ?? '',
      );

  @override
  String? toJson(City? object) =>
      object == null ? null : EnumToString.convertToString(object);
}

JsonConverterをインターフェースとして CityEnumConverter というものを実装しています

fromJsonStringからEnum(City)に、 toJsonEnum(City)からStringに変換する処理になっています。

では上記で実装したコンバータをどう使うのかというと、このようにします

@CityEnumConverter() City? city, // ← `@CityEnumConverter()`をつける

全体のコードが↓

enum City {
  tokyo,
  osaka,
  fukuoka,
}

@freezed
abstract class Profile with _$Profile {
  const factory Profile({
    String? name,
    @CityEnumConverter() City? city, // ← `@CityEnumConverter()`をつける
  }) = _Profile;

  factory Profile.fromJson(Map<String, dynamic> json) =>
      _$ProfileFromJson(json);
}

class CityEnumConverter implements JsonConverter<City?, String?> {
  const EnumConverter();

  @override
  City? fromJson(String? json) => EnumToString.fromString(
        City.values,
        json ?? '',
      );

  @override
  String? toJson(City? object) =>
      object == null ? null : EnumToString.convertToString(object);
}

このように、一度実装してしまえば使いたいところで @JsonConverterを実装したクラス() と宣言するだけで自動的にコンバートしてくれるようになります。

実際に動かしてみます

void main() {
  final jsonData = {
    'name': 'rei',
    'city': 'tokyo',
  };

  final profile = Profile.fromJson(jsonData);
  print(profile);

  final json = profile.toJson();
  print(json);
}

ログ↓

I/flutter ( 9860): profile: Profile(name: rei, city: City.tokyo)
I/flutter ( 9860): json: {name: rei, city: tokyo}

と、ちゃんとそれぞれ変換されているのがわかりますね

このようにJsonConverterを実装したクラスを作成すると、便利なカスタムコンバータができるよというお話でした

おまけ

今回紹介したJsonConverterなんですが、知っておくと便利なものがあるのでちょっと紹介しようと思います

それがこちら
TimestampConverter

これは何かというと、 Firestore のパッケージの中に入っているカスタムコンバータです。

実装見てみるとこんな感じ

class TimestampConverter implements JsonConverter<DateTime?, Timestamp?> {
  const TimestampConverter();

  @override
  DateTime? fromJson(Timestamp? json) => json?.toDate();

  @override
  Timestamp? toJson(DateTime? object) =>
      object == null ? null : Timestamp.fromDate(object);
}

見てわかる通り、Firestoreで日時を管理するときに利用する Timestamp をDartのDateTimeに変換してくれるものになります。

使用方法は今回の記事で作成したCityEnumConverterと同じように変換させたいパラメータの先頭に @TimestampConverter() と宣言するだけです。

@freezed
abstract class Profile with _$Profile {
  const factory Profile({
    String? name,
    @CityEnumConverter() City? city,
    @TimestampConverter() DateTime? birthDate, // ←@TimestampConverter()をつける
  }) = _Profile;

  factory Profile.fromJson(Map<String, dynamic> json) =>
      _$ProfileFromJson(json);
}

このTimestampConverterを使えば、Firestoreのデータをパースしたい時、Timestampを考慮してゴニョゴニョとする必要がなくなります。

最後に

Flutterでモデルクラスを作成するときFreezedを使用することが多いと思います。
そんな中でJsonConverterを使うともっと便利にできるよということを知っていただければ幸いです。

14
11
0

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
14
11