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

如何序列化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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 20:50:29