You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 19:57:21