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

如何让Freezed使用Redirect.fromPath工厂?JSON反序列化问题求助

问题描述

我定义了如下Freezed类:

@freezed
class Lobby with _$Lobby implements DataObject {
  @JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
  const factory Lobby({
    Redirect? redirect,
  }) = _Lobby;
  const Lobby._();

  factory Lobby.fromJson(Map<String, dynamic> json) => _$LobbyFromJson(json);
}

以及如下枚举:

enum Redirect {
  test1('/thisistest1'),
  test2('/thisistest2');

  const Redirect(this.path);

  factory Redirect.fromPath(String path) {
    return values.firstWhere((e) => e.path == path);
  }

  final String path;
}

调用Lobby.fromJson构造Lobby类时,JSON数据中仅包含path字符串而非Redirect枚举值,需要进行转译。同时存在边缘场景:当JSON数据为{redirect: null}时,调用Lobby.fromJson(data)需要正确处理该情况。

请问能否让Freezed使用Redirect.fromPath工厂方法,或修改fromJson方法实现转译?最优方案是什么?


最优解决方案:使用JsonConverter

推荐通过自定义JsonConverter实现字符串到Redirect枚举的转译,这是Freezed/JsonSerializable体系下最规范、可复用的方案,同时能自然处理null场景。

步骤1:实现自定义JsonConverter

class RedirectConverter implements JsonConverter<Redirect?, dynamic> {
  const RedirectConverter();

  @override
  Redirect? fromJson(dynamic json) {
    // 处理null场景:json为null时直接返回null
    if (json == null) return null;
    // 非null时调用枚举的fromPath方法完成转译
    return Redirect.fromPath(json as String);
  }

  @override
  dynamic toJson(Redirect? object) {
    // 序列化时返回枚举的path字段,或null
    return object?.path;
  }
}

步骤2:在Freezed类中应用转换器

有两种应用方式可选:

方式一:全局应用(所有Redirect字段生效)

在@JsonSerializable注解中添加converters参数:

@freezed
class Lobby with _$Lobby implements DataObject {
  @JsonSerializable(
    fieldRename: FieldRename.snake,
    explicitToJson: true,
    converters: [RedirectConverter()], // 全局添加转换器
  )
  const factory Lobby({
    Redirect? redirect,
  }) = _Lobby;
  const Lobby._();

  factory Lobby.fromJson(Map<String, dynamic> json) => _$LobbyFromJson(json);
}

方式二:仅对单个字段应用

如果只需要特定字段使用该转换器,直接在字段上标注:

@freezed
class Lobby with _$Lobby implements DataObject {
  @JsonSerializable(fieldRename: FieldRename.snake, explicitToJson: true)
  const factory Lobby({
    @RedirectConverter() Redirect? redirect, // 字段级标注转换器
  }) = _Lobby;
  const Lobby._();

  factory Lobby.fromJson(Map<String, dynamic> json) => _$LobbyFromJson(json);
}

额外优化:处理异常场景

如果JSON传入了不存在的path,values.firstWhere会抛出异常,建议在fromPath中添加兜底逻辑:

factory Redirect.fromPath(String path) {
  return values.firstWhere(
    (e) => e.path == path,
    orElse: () => Redirect.test1, // 可替换为自定义异常或默认值
  );
}

其他可选方案:手动修改fromJson方法

如果不想使用转换器,也可以手动包装自动生成的序列化方法,处理redirect字段:

factory Lobby.fromJson(Map<String, dynamic> json) {
  final redirectJson = json['redirect'];
  if (redirectJson != null && redirectJson is String) {
    json = Map.from(json)..['redirect'] = Redirect.fromPath(redirectJson);
  }
  return _$LobbyFromJson(json);
}

但该方案缺点明显:若Lobby字段较多,维护成本高,转译逻辑无法复用,远不如JsonConverter优雅。


内容的提问来源于stack exchange,提问作者progNewbie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:46:34