概要
どうも、@daiki1003です!みなさま、Flutter生活はいかがでしょうか?
今回は、ある程度の規模感のアプリになると必須と言っても過言ではないjson_annotationパッケージに含まれているJsonKeyについての解説記事になります。
・JsonKeyなにそれ?
・なんとなく使ってるけど知らないことがあるかも?
なんて人に役に立つような記事にしようと思っています。
それでは行ってみましょー!
執筆時環境
Flutter: 3.7.1Dart: 2.19.1json_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
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!



コメント