如何序列化Dart增强枚举的所有字段?Freezed适配问题
解决Freezed+json_serializable中增强枚举的多字段序列化问题
核心问题
默认情况下,json_serializable对枚举的序列化仅支持基于单字段(如name/index)的转换,但Java后端返回的是包含枚举所有字段(type+name)的JSON对象,因此需要自定义多字段匹配的序列化逻辑,同时保留增强枚举的类型安全优势。
最优实现方案:使用JsonConverter自定义转换器
这种方式无需在每个使用枚举的地方重复写@JsonKey,逻辑集中且复用性强,完全保留枚举特性。
1. 定义增强枚举
enum WorkingMode { online(type: 'O', name: '在线'), offline(type: 'F', name: '离线'); final String type; final String name; const WorkingMode({required this.type, required this.name}); }
2. 创建枚举转换器
实现JsonConverter,处理多字段的序列化/反序列化逻辑:
import 'package:json_annotation/json_annotation.dart'; class WorkingModeConverter implements JsonConverter<WorkingMode, Map<String, dynamic>> { const WorkingModeConverter(); @override WorkingMode fromJson(Map<String, dynamic> json) { // 根据后端返回的type和name匹配枚举实例 return WorkingMode.values.firstWhere( (mode) => mode.type == json['type'] && mode.name == json['name'], orElse: () => throw ArgumentError('无效的WorkingMode配置: $json'), ); } @override Map<String, dynamic> toJson(WorkingMode mode) { // 序列化时返回包含所有字段的Map return {'type': mode.type, 'name': mode.name}; } }
3. 在Freezed类中应用转换器
在枚举列表字段上添加转换器注解:
import 'package:freezed_annotation/freezed_annotation.dart'; part 'test.freezed.dart'; part 'test.g.dart'; @freezed class Test with _$Test { const factory Test({ // 应用自定义转换器 @WorkingModeConverter() required List<WorkingMode> modes, }) = _Test; factory Test.fromJson(Map<String, dynamic> json) => _$TestFromJson(json); }
4. 生成序列化代码
运行build_runner命令生成所需的序列化代码:
flutter pub run build_runner build
为什么@JsonValue/@JsonEnum无法解决?
@JsonValue只能指定单个字段作为枚举的序列化标识,无法处理多字段匹配的场景。@JsonEnum的valueField参数同样仅支持单字段映射,不支持基于多个字段的枚举实例匹配。
替代方案:直接在枚举中实现fromJson/toJson
如果不想用转换器,也可以直接在枚举中定义静态方法,然后通过@JsonKey指定:
// 在WorkingMode枚举中添加 static WorkingMode fromJson(Map<String, dynamic> json) { return WorkingMode.values.firstWhere( (mode) => mode.type == json['type'] && mode.name == json['name'], ); } Map<String, dynamic> toJson() => {'type': type, 'name': name}; // 在Test类中使用 @freezed class Test with _$Test { const factory Test({ @JsonKey(fromJson: WorkingMode.fromJson, toJson: (WorkingMode m) => m.toJson()) required List<WorkingMode> modes, }) = _Test; factory Test.fromJson(Map<String, dynamic> json) => _$TestFromJson(json); }
这种方式适合仅在少数地方使用枚举的场景,但复用性不如转换器方案。
内容的提问来源于stack exchange,提问作者rocotocloc
相关产品推荐
相关产品推荐

