如何修复Freezed中Map转List自定义JsonConverter不生效问题?
问题描述
我有如下使用freezed定义的模型:
@freezed class User with _$User { const factory User({ required UserID id, required List<String> categories, }) = _User; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); }
希望从如下JSON对象反序列化该模型:
{ "id": "1234", "categories": { "A": true, "B": true } }
显然默认序列化无法生效,因此我尝试实现自定义JsonConverter:
class ListMapConverter<K, V> implements JsonConverter<List<K>, Map<K, V>> { const ListMapConverter(this._value); final V _value; @override List<K> fromJson(Map<K, V> json) => json.keys.toList(); @override Map<K, V> toJson(List<K> object) => {for (final e in object) e: _value}; }
我按如下方式标注模型的属性:
@ListMapConverter<String, bool>(true) required List<String> categories
但这并未生效,生成的代码中并未使用该自定义转换器:
_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl( id: UserID.fromJson(json['id'] as String), categories: (json['categories'] as List<dynamic>) .map((e) => e as String) .toList(), ); Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) => <String, dynamic>{ 'id': instance.id, 'categories': instance.categories, };
我该如何修复此问题?使用freezed/json_serializable在Map与List之间转换是否有更优方案?
解决方案
1. 修复转换器未生效的问题
核心问题是泛型转换器的类型参数未被json_serializable正确识别,需做以下调整:
调整转换器定义
由于JSON解析后默认是Map<String, dynamic>类型,而非泛型Map<K,V>,需修改转换器适配该类型:
class ListMapConverter extends JsonConverter<List<String>, Map<String, dynamic>> { const ListMapConverter(this.targetValue); final bool targetValue; @override List<String> fromJson(Map<String, dynamic> json) { return json.keys.where((key) => json[key] == targetValue).toList(); } @override Map<String, dynamic> toJson(List<String> object) { return {for (final category in object) category: targetValue}; } }
正确标注模型属性
将转换器标注放在字段类型前方,确保freezed能正确识别:
@freezed class User with _$User { const factory User({ required UserID id, @ListMapConverter(true) required List<String> categories, }) = _User; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); }
重新生成代码
执行命令重新生成序列化逻辑:
flutter pub run build_runner build --delete-conflicting-outputs
此时生成的代码会正确调用转换器:
_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl( id: UserID.fromJson(json['id'] as String), categories: const ListMapConverter(true).fromJson(json['categories'] as Map<String, dynamic>), ); Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) => <String, dynamic>{ 'id': instance.id, 'categories': const ListMapConverter(true).toJson(instance.categories), };
2. 更优方案:使用@JsonKey回调
如果仅针对单个字段的转换,无需定义转换器类,直接用@JsonKey指定转换函数更轻量:
@freezed class User with _$User { const factory User({ required UserID id, @JsonKey( fromJson: _categoriesFromJson, toJson: _categoriesToJson, ) required List<String> categories, }) = _User; factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); } List<String> _categoriesFromJson(Map<String, dynamic> json) { return json.keys.where((k) => json[k] == true).toList(); } Map<String, dynamic> _categoriesToJson(List<String> categories) { return {for (final c in categories) c: true}; }
内容的提问来源于stack exchange,提问作者katkak
相关产品推荐
相关产品推荐

