JSON のキー名に一部、 camelCase のものが残っていた。のであった・・・
Build configuration で field_rename
を指定して snake_case → camelCase に変換する場合、 JSON のキー名に snake_case が残っているとその部分が読み込まれないので注意が必要。
背景
freezed
freezed を使用するとクラス定義を簡便に行える( Java での Lombok に相当)。そのなかには fromJson
も定義できる。これが素晴らしい。
final List<Map<String, dynamic>> dummyJson = [
{
'key_a': 'aaa',
'key_b': 'bbb',
},
{
'key_a': 'AAA',
'key_b': 'BBB',
},
];
final List<MyEntity> entity = dummyJson.map((json) => MyEntity.fromJson(json)).toList();
このようなことができる。
Build configuration
標準だと fromJson
メソッドはメンバ変数名と JSON のキー名を突合する。上のような JSON をパースする場合、クラス定義側は下のようになるだろうか。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'data.freezed.dart';
part 'data.g.dart';
@freezed
class MyEntity with _$MyEntity {
factory MyEntity.fromJson(Map<String, dynamic> json) => _$MyEntityFromJson(json);
const factory AnkiCard({
required String key_a,
required String key_b,
}) = _MyEntity;
}
これではメンバ変数定義が snake_case になってしまい、いかにもよろしくない。jsonSerializable の 🔗Build configuration を指定する ことで JSON の snake_case を camelCase に変換して対応づけられる。 build.yaml
を作成した。
targets:
$default:
builders:
json_serializable:
options:
any_map: false
checked: false
constructor: ""
create_factory: true
create_field_map: false
create_to_json: true
disallow_unrecognized_keys: false
explicit_to_json: false
field_rename: snake # これ
generic_argument_factories: false
ignore_unannotated: false
include_if_null: true
field_rename
の 🔗値 を指定した。これでクラス定義側は camelCase でメンバ変数を定義できる。よかったよかった。
エラー発生。
残念ながら万事解決とはならず、 MyEntity クラスの利用時にぬるぽが発生した。
error: TypeError: null: type 'Null' is not a subtype of type 'String'
昨日までロードできていたのに。
g.dart
ひと通り検索して分からないことを確かめてから、ひとまず生成された freezed のファイルを読むことにした。
_$MyEntityImpl _$$MyEntityImplFromJson(Map<String, dynamic> json) =>
_$MyEntityImpl(
keyA: json['key_a'] as String,
keyB: json['key_b'] as String,
);
ここを読んで唐突に気付いた。 JSON データの中に一部 camelCase が残っていた、と・・・。
おわり
話が分かりやすいように単純化して書いているので「そんなミスあるかいな」と思われるかもしれない。しかしいくつかのことを並行していたり、ほかに怪しい部分を疑ってしまい、このようなことになった。