12
4

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 1 year has passed since last update.

Dart 2.17のEnhanced enumsを使いenumのJSONエンコードをスマートに書く

Posted at

TL;DR

  • enumにtoJsonメソッドを生やすと、enumを任意の値にJSONエンコードできる
  • Enhanced enumsは最高

Dart 2.17 でenumが大幅に強化された

enumが通常のclassのように扱えるようになりました。
任意のコンストラクタをもたせたり、フィールドやメソッドを生やしたり、既存メソッドのオーバーライドなどができるようになりました。

詳しくは下記の記事がわかりやすいです。
https://medium.com/dartlang/dart-2-17-b216bfc80c5d

enum -> JSONは今まで簡単にできなかった

Humanクラスを考えてみます。
Humanクラスは2つのフィールドを持ちます。int型のageと、Gender型のgenderです。
Gender型というのがenumになります。

enum Gender {
  female,
  male
}

class Human {
  final int age;
  final Gender gender;
  
  Human({required this.age, required this.gender});
  
  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }
}

カスタムクラスなのでtoJSONメソッドを追加しています。

これをjson.encodeすると、以下のようなエラーになります。

Uncaught Error: Converting object to an encodable object failed: Instance of 'Human'

Humanオブジェクトのエンコードに失敗しています。
なぜならenumのGender型であるgenderフィールドをエンコードする方法を知らないからです。

例えば、Gender.female -> 1, Gender.male -> 2 という値にエンコードしたいとしましょう。
以下のように書くことで、取り急ぎやりたいことは実現できるでしょう。

  toJson(){
    return {
      "age": age,
      "gender": gender == Gender.female ? 1 : 2 // 追加
    };
  }

ただ、Humanクラスの中でGenderの値をどうエンコードするかというビジネスロジックが入ってしまっているのは気持ち悪いです。また、Gender型を含むクラスが複数あったら、そのJSONエンコードの全箇所で同じ変換処理を書かなければ行けません。避けたいですよね。

このように、今まではenumを変換する際に悩ましい問題がありました。

Dart 2.17のenumではこう書く

そこでDart 2.17のEnhanced enumsの出番です!
Dart 2.17では、Gender型を以下のようにアップデートできます。

enum Gender {
  female(1), // enumを独自コンストラクタで初期化できる!
  male(2);
  
  final int value; // enum値ごとにカスタム値を持てる!
  
  const Gender(this.value); // 独自コンストラクタが定義できる!
  
  dynamic toJson() => value; // フィールドメソッドを生やせる!
}

female = 1, male = 2 であることがコードから明確に読み取れるのも良いですね。

このアップデートにより、HumanクラスのtoJsonメソッドは以下のように書くことができます。

  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }

最高にシンプル!美しいです!
gender型を変換するための処理がきれいに無くなりました。

ではこのクラスを使ってみましょう。

void main() {
  final me = Human(age: 37, gender: Gender.male);
  final jsonStr = json.encode(me);
  print(jsonStr); // -> {"age":37,"gender":2}
}

enum型がちゃんと数字に変換できています。
Enhanced enums、最高!

コード全体

import 'dart:convert';

enum Gender {
  female(1),
  male(2);
  
  final int value;
  
  const Gender(this.value);
  
  dynamic toJson() => value;
}

class Human {
  final int age;
  final Gender gender;
  
  Human({required this.age, required this.gender});
  
  toJson(){
    return {
      "age": age,
      "gender": gender
    };
  }
}

void main() {
  final me = Human(age: 37, gender: Gender.male);
  final obj = json.encode(me);
  print(obj); // -> {"age":37,"gender":2}
  assert(obj == '{"age":37,"gender":2}');
}
12
4
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
12
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?