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}');
}