概要
どうも、@daiki1003です!みなさま、Flutter生活はいかがでしょうか?
今回は、ある程度の規模感のアプリになると必須と言っても過言ではないjson_annotation
パッケージに含まれているJsonKey
についての解説記事になります。
・JsonKey
なにそれ?
・なんとなく使ってるけど知らないことがあるかも?
なんて人に役に立つような記事にしようと思っています。
それでは行ってみましょー!
執筆時環境
Flutter
: 3.7.1
Dart
: 2.19.1
json_annotation
: 4.8.0
JsonKeyの概要
JsonKey
はその名の通り、JSON
のキー
に対するエンコード/デコード
に関する細かい調整を行うためのアノテーションです。
JsonKey
は以下のようなクラス定義となっています。
@Target({TargetKind.field, TargetKind.getter}) class JsonKey { final Object? defaultValue; final bool? disallowNullValue; final Function? fromJson; final bool? includeFromJson; final bool? includeIfNull; final bool? includeToJson; final String? name; final Object? Function(Map, String)? readValue; final bool? required; final Function? toJson; final Enum? unknownEnumValue; const JsonKey({ this.defaultValue, this.disallowNullValue, this.fromJson, this.includeFromJson, this.includeIfNull, this.includeToJson, this.name, this.readValue, this.required, this.toJson, this.unknownEnumValue, }); static const Enum nullForUndefinedEnumValue = _NullAsDefault.value; }
例えば、 @JsonKey(defaultValue: 1)
のように指定することができます。
これ以降では、それぞれのプロパティに何が指定出来てその結果どうなるかについて詳しく解説していこうと思います。
各パラメタの説明
Object? defaultValue
JSON
にキーが含まれていなかった場合の値を指定します。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(defaultValue: 'defaultName') required String name, }); }
結果
// hoge.g.dart _$Hoge _$$HogeFromJson(Mapjson) => _$Hoge( name: json['name'] as String? ?? 'defaultName', );
また、 default"Value"
とありますが、型が同じであれば、トップレベルの関数や引数を取らないコンストラクタも指定することができます。
String calculate() { return 'defaultName'; } @freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(defaultValue: calculate) required String name, }); }
結果
// hoge.g.dart _$Hoge _$$HogeFromJson(Mapjson) => _$Hoge( name: json['name'] as String? ?? calculate(), );
bool? disallowNullValue
true
を指定した場合、JSON
にキーがあるにも関わらず値がnull
だった際に、DisallowedNullValueException
をthrow
します。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(disallowNullValue: true) required String name, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) { $checkKeys( json, disallowNullValues: const ['name'], ); return _$_Hoge( name: json['name'] as String, ); }
Function? fromJson/toJson
JSON
からのエンコード/デコードの際に用いるメソッドです。
必ず一つの引数を取り、該当のプロパティの型を返してください。
DateTime fromTimestamp(int timestamp) { return DateTime.fromMillisecondsSinceEpoch(timestamp * 1000); } int toTimestamp(DateTime createdAt) { return createdAt.millisecondsSinceEpoch ~/ 1000; } @freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(fromJson: fromTimestamp, toJson: toTimestamp) required DateTime createdAt, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) => _$_Hoge( createdAt: fromTimestamp(json['created_at'] as int), ); Map _$$_HogeToJson(_$_Hoge instance) => { 'created_at': toTimestamp(instance.createdAt), };
bool? includeFromJson/includeToJson
現在、最新である4.8.0
で追加されたので情報が少なそうですね。
指定したプロパティをエンコード/デコードするかというフラグになります。null
の際は、対象のプロパティがpublic
ならtrue
、private
ならfalse
と同様の意味になります。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(includeToJson: false) required String name, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) => _$_Hoge( name: json['name'] as String? ?? '', ); // JSON化する際に、'name'を含まない Map _$$_HogeToJson(_$_Hoge instance) => {};
bool? includeIfNull
対象のプロパティがnull
だった際に、JSON
にキーを含めるかどうかを指定します。includeIfNull
がnull
の場合は、true
と同様です。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(includeIfNull: false) String? name, }); }
結果
Map_$$_HogeToJson(_$_Hoge instance) { final val = {}; void writeNotNull(String key, dynamic value) { if (value != null) { val[key] = value; } } writeNotNull('name', instance.name); return val; }
String? name
未指定の場合、対象のプロパティと同名のキーをデコードし、同名のキーとしてエンコードします。
今までの例では、name
と言うプロパティを'name'
と言うキーで扱っていました。
これを別名にしたい場合、name
を指定することで実現できます。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(name: 'user_name') required String name, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) => _$_Hoge( name: json['user_name'] as String?, ); Map _$$_HogeToJson(_$_Hoge instance) => { 'user_name': instance.name, };
Object? Function(Map, String)? readValue
json[key]
以外で、プロパティのデコードを行いたい際に、使う関数を渡すことが出来ます。
あまり良い例ではないのですが、以下のような使い方ができると言うことです。
財布の中身が、カート内の合計金額より多ければ購入可能という判定をしています。
bool calculateCanBuy(Mapjson, String _) { final cartJson = json['cart'] as Map ; final walletJson = json['wallet'] as Map ; return (cartJson['current_price'] as int) <= (walletJson['money'] as int); } @freezed class User with _$User { const factory User({ @Default(false) @JsonKey(readValue: calculateCanBuy) bool canBuy, required String name, required Wallet wallet, required Cart cart, }) = _User; factory User.fromJson(Map json) => _$UserFromJson(json); } @freezed class Wallet with _$Wallet { const factory Wallet({ required int money, }) = _Wallet; factory Wallet.fromJson(Map json) => _$WalletFromJson(json); } @freezed class Cart with _$Cart { const factory Cart({ required int currentPrice, }) = _Cart; factory Cart.fromJson(Map json) => _$CartFromJson(json); }
結果
_$_User _$$_UserFromJson(Mapjson) => _$_User( canBuy: calculateCanBuy(json, 'can_buy') as bool? ?? false, ... );
bool? required
true
を指定すると、JSON
からのデコードの際に該当のキーが含まれているかどうかをチェックします。
含まれていない場合は、MissingRequiredKeysException
をthrow
します。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(required: true) required String name, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) { $checkKeys( json, requiredKeys: const ['name'], ); return _$_Hoge( name: json['name'] as String, ); }
Enum? unknownEnumValue
対象プロパティがenum
の際にのみ指定できます。
未知のenum
値の時はここで指定した値としてデコードされます。
enum Sports { baseball, soccer, unknown, } @freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(unknownEnumValue: Sports.unknown) required Sports sports, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) => _$_Hoge( sports: $enumDecode(_$SportsEnumMap, json['sports'], unknownValue: Sports.unknown), );
もし、未知のenum
値の時は、null
にしておいてほしい場合は、JsonKey.nullForUndefinedEnumValue
を指定します。
@freezed class Hoge with _$Hoge { const factory Hoge({ @JsonKey(unknownEnumValue: JsonKey.nullForUndefinedEnumValue) Sports? sports, }); }
結果
_$_Hoge _$$_HogeFromJson(Mapjson) => _$_Hoge( sports: $enumDecodeNullable(_$SportsEnumMap, json['sports'], unknownValue: JsonKey.nullForUndefinedEnumValue), );
最後に
いかがでしたでしょうか?
一つでも知らなかった挙動やプロパティがあれば嬉しいなと思います!
JsonSerializable
に関しても同様の記事を書こうかなと思っているのでお楽しみに!
Twitterフォローお願いします
「次回以降も記事を読んでみたい!」「この辺分からなかったから質問したい!」
そんな時は、是非Twitter (@daiki1003)やInstagram (@ashdik_flutter)のフォローお願いします♪
Twitterコミュニティ参加お願いします
Twitterコミュニティ「Flutter lovers」を開設しました!参加お待ちしております😁
☕️ Buy me a coffee
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!
コメント