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

如何修复Freezed中Map转List自定义JsonConverter不生效问题?

问题描述

我有如下使用freezed定义的模型:

@freezed
class User with _$User {
  const factory User({
    required UserID id,
    required List<String> categories,
  }) = _User;

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

希望从如下JSON对象反序列化该模型:

{
  "id": "1234",
  "categories": {
    "A": true,
    "B": true
  }
}

显然默认序列化无法生效,因此我尝试实现自定义JsonConverter:

class ListMapConverter<K, V> implements JsonConverter<List<K>, Map<K, V>> {
  const ListMapConverter(this._value);

  final V _value;

  @override
  List<K> fromJson(Map<K, V> json) => json.keys.toList();

  @override
  Map<K, V> toJson(List<K> object) => {for (final e in object) e: _value};
}

我按如下方式标注模型的属性:

@ListMapConverter<String, bool>(true) required List<String> categories

但这并未生效,生成的代码中并未使用该自定义转换器:

_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl(
      id: UserID.fromJson(json['id'] as String),
      categories: (json['categories'] as List<dynamic>)
          .map((e) => e as String)
          .toList(),
    );

Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) =>
    <String, dynamic>{
      'id': instance.id,
      'categories': instance.categories,
    };

我该如何修复此问题?使用freezed/json_serializable在Map与List之间转换是否有更优方案?


解决方案

1. 修复转换器未生效的问题

核心问题是泛型转换器的类型参数未被json_serializable正确识别,需做以下调整:

调整转换器定义

由于JSON解析后默认是Map<String, dynamic>类型,而非泛型Map<K,V>,需修改转换器适配该类型:

class ListMapConverter extends JsonConverter<List<String>, Map<String, dynamic>> {
  const ListMapConverter(this.targetValue);

  final bool targetValue;

  @override
  List<String> fromJson(Map<String, dynamic> json) {
    return json.keys.where((key) => json[key] == targetValue).toList();
  }

  @override
  Map<String, dynamic> toJson(List<String> object) {
    return {for (final category in object) category: targetValue};
  }
}

正确标注模型属性

将转换器标注放在字段类型前方,确保freezed能正确识别:

@freezed
class User with _$User {
  const factory User({
    required UserID id,
    @ListMapConverter(true) required List<String> categories,
  }) = _User;

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

重新生成代码

执行命令重新生成序列化逻辑:

flutter pub run build_runner build --delete-conflicting-outputs

此时生成的代码会正确调用转换器:

_$UserImpl _$$UserImplFromJson(Map<String, dynamic> json) => _$UserImpl(
      id: UserID.fromJson(json['id'] as String),
      categories: const ListMapConverter(true).fromJson(json['categories'] as Map<String, dynamic>),
    );

Map<String, dynamic> _$$UserImplToJson(_$UserImpl instance) =>
    <String, dynamic>{
      'id': instance.id,
      'categories': const ListMapConverter(true).toJson(instance.categories),
    };

2. 更优方案:使用@JsonKey回调

如果仅针对单个字段的转换,无需定义转换器类,直接用@JsonKey指定转换函数更轻量:

@freezed
class User with _$User {
  const factory User({
    required UserID id,
    @JsonKey(
      fromJson: _categoriesFromJson,
      toJson: _categoriesToJson,
    ) required List<String> categories,
  }) = _User;

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

List<String> _categoriesFromJson(Map<String, dynamic> json) {
  return json.keys.where((k) => json[k] == true).toList();
}

Map<String, dynamic> _categoriesToJson(List<String> categories) {
  return {for (final c in categories) c: true};
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 13:36:23