Dart json_serializable反序列化嵌套同名字段报重复key如何解决
问题场景
待反序列化的JSON响应结构如下:
{ "name": "A name", "international": { "name": { "en": "A name", "fr": "Un nom" } } }
编写的Dart反序列化实现代码如下:
import 'package:json_annotation/json_annotation.dart'; import 'dart:convert'; part 'model.g.dart'; void main() { var json = '''{ "name": "A name", "international": { "name": { "en": "A name", "fr": "Un nom" } } }'''; var model = Model.fromJson(jsonDecode(json)); } @JsonSerializable() class Model { Model({ required this.name, required this.internationalNames, }); factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json); final String name; @JsonKey(readValue: _readInternational, name: "name") final Map<String, String> internationalNames; static Object? _readInternational(Map<dynamic, dynamic> json, String key) => json['international'][key]; }
运行build_runner执行代码生成时抛出如下报错:
More than one field has the JSON key for name "name"
报错原因
json_serializable的字段键名校验逻辑仅识别@JsonKey注解中name属性声明的键名,不会解析readValue自定义的取值路径。上述代码中根层级的name字段、自定义取值的internationalNames字段都声明了name: "name",生成器默认判定两个字段映射根层级下的同名键,直接抛出重复键错误,和实际取值路径是否嵌套无关。
修复方案
移除internationalNames字段@JsonKey注解中的name参数即可。readValue回调已经完全接管该字段的取值逻辑,不需要额外声明映射键名,也就不会触发根层级的键名重复校验。
修改后的Model核心代码:
@JsonSerializable() class Model { Model({ required this.name, required this.internationalNames, }); factory Model.fromJson(Map<String, dynamic> json) => _$ModelFromJson(json); final String name; // 移除name属性,仅保留自定义取值逻辑 @JsonKey(readValue: _readInternational) final Map<String, String> internationalNames; static Object? _readInternational(Map<dynamic, dynamic> json, String key) => json['international']['name']; }
扩展优化
如果后续需要映射international节点下的多个字段,可以把_readInternational方法改造成通用取值逻辑,不需要为每个字段单独编写取值函数:
static Object? _readInternational(Map<dynamic, dynamic> json, String key) { final internationalNode = json['international'] as Map?; return internationalNode?[key]; }
使用时遵循以下规则即可:
- 给
international节点下的字段配置@JsonKey(name: '目标键名')时,传入的键名会作为key参数传入回调,自动到嵌套节点下查找 - 这种写法下字段配置的
name不会参与根层级的键名校验,不会和根层级同名字段冲突 - 如果没有配置
name参数,回调会默认接收字段名按全局规则转换后的JSON键名(默认是驼峰转蛇形)作为key参数
内容的提问来源于stack exchange,提问作者Cevilur
相关产品推荐
相关产品推荐

