はじめに
Flutterアプリの開発などでよく利用される freezed を利用する際のハマりポイントとその対応方法をまとめています。
1. json_serializableで、キーの指定が正しいはずなのにデコードができない
JSONでの命名とクラスのプロパティ名が一致している場合は基本的にはデコードが成功するはずです。
しかし、build.yamlで field_rename
が指定されている場合、これがうまく動かないケースがあります。
targets:
$default:
builders:
# ...
json_serializable:
options:
# ...
field_rename: snake # none => スネークケース
# ...
part 'some_response.freezed.dart';
part 'some_response.g.dart';
@freezed
class SomeResponse with _$SomeResponse {
const factory SomeResponse({
required int someValue, // キャメルケース
}) = _SomeResponse;
factory SomeResponse.fromJson(Map<String, dynamic> json) =>
_$SomeResponseFromJson(json);
}
field_rename
の方を変更してしまうとプロジェクト全体に影響が及んでしまうため、この場合は対象のプロパティに @JsonKey
を指定してキー名を明示することで問題を回避することが可能です。
part 'some_response.freezed.dart';
part 'some_response.g.dart';
@freezed
class SomeResponse with _$SomeResponse {
const factory SomeResponse({
- required int someValue,
+ @JsonKey(name: 'someValue') required int someValue,
}) = _SomeResponse;
factory SomeResponse.fromJson(Map<String, dynamic> json) =>
_$SomeResponseFromJson(json);
}
2. json_serializableで、ルートがリストのJSONをデコードできない
以下のようなルートがArrayのJSONをデコードする場合、
[
1,
2
]
これを下記定義のfromJSONにそのまま渡すとデコードがうまくできません。
part 'some_response.freezed.dart';
part 'some_response.g.dart';
@freezed
class SomeResponse with _$SomeResponse {
const factory SomeResponse({
@Default(<int>[]) List<int> ids,
}) = _SomeResponse;
factory SomeResponse.fromJson(Map<String, dynamic> json) =>
_$SomeResponseFromJson(json);
}
final jsonMap = json.decode(response.body) as JsonMap;
final response = SomeResponse.fromJson(jsonMap); // 正しくデコードされない
この形式のデータに対応するには、Dart側で定義したプロパティ名に対応したキーをルートに追加した上でデコードを行うと正しく変換することができます。
- final jsonMap = json.decode(response.body) as JsonMap;
+ final jsonMap = json.decode('{"ids": ${response.body}}') as JsonMap;
final response = SomeResponse.fromJson(jsonMap);