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

Dart中如何用泛型优雅反序列化单/列表类型JSON API响应

Dart中API响应泛型解码的列表适配问题

现有一个处理API响应的泛型类APIResponse<T>,代码如下:

class APIResponse<T> {
  final int? statusCode;
  final T? data;
  final APIError? error;

  APIResponse({this.statusCode, this.data, this.error});

  factory APIResponse.fromJson(
    Map<String, dynamic> json, T Function(Map<String, dynamic>) fromJsonData
  ) {
    if (json['data'] != null) {
      if (json['data'] is List) {
        // 此处无法直接用fromJsonData处理列表
      } else {
        // 处理单个对象正常
      }      
    }
    // 省略其他逻辑
  }
}

当T为单个对象类型(如User)时,APIResponse<User>能正常通过fromJsonData解码单个对象;但当T为List<User>时,传入的fromJsonData只能解码单个User,无法直接适配列表类型,导致类型不匹配。目前通过新增List<T>? dataList字段解决,现寻求更优雅的原生方案或第三方JSON解码器。


优雅的原生解决方案

1. 调整工厂方法参数,让解码逻辑更灵活

修改fromJson的fromJsonData参数类型为T Function(dynamic),允许调用方自行处理单个对象或列表的解码逻辑,无需新增类字段:

class APIResponse<T> {
  final int? statusCode;
  final T? data;
  final APIError? error;

  APIResponse({this.statusCode, this.data, this.error});

  factory APIResponse.fromJson(
    Map<String, dynamic> json, 
    T Function(dynamic) fromJsonData,
  ) {
    final rawData = json['data'];
    final decodedData = rawData != null ? fromJsonData(rawData) : null;

    return APIResponse(
      statusCode: json['statusCode'],
      data: decodedData,
      error: json['error'] != null ? APIError.fromJson(json['error']) : null,
    );
  }
}

使用示例:

  • 单个对象场景:
APIResponse<User>.fromJson(
  json,
  (data) => User.fromJson(data as Map<String, dynamic>),
);
  • 列表对象场景:
APIResponse<List<User>>.fromJson(
  json,
  (data) => (data as List).map((item) => User.fromJson(item as Map<String, dynamic>)).toList(),
);

2. 封装列表解码工具函数简化调用

如果频繁处理列表解码,可以单独封装工具函数减少重复代码:

List<T> decodeList<T>(dynamic rawList, T Function(Map<String, dynamic>) fromJsonItem) {
  return (rawList as List).map((item) => fromJsonItem(item as Map<String, dynamic>)).toList();
}

列表场景调用时可简化为:

APIResponse<List<User>>.fromJson(
  json,
  (data) => decodeList<User>(data, User.fromJson),
);

第三方JSON解码器推荐

json_serializable(代码生成式解码)

通过代码生成自动生成序列化逻辑,配合泛型参数工厂,可轻松适配单个和列表类型的响应。

  1. 添加依赖到pubspec.yaml:
dependencies:
  json_annotation: ^4.8.1

dev_dependencies:
  build_runner: ^2.4.4
  json_serializable: ^6.7.0
  1. 为APIResponse添加序列化注解:
import 'package:json_annotation/json_annotation.dart';

part 'api_response.g.dart';

@JsonSerializable(genericArgumentFactories: true)
class APIResponse<T> {
  final int? statusCode;
  final T? data;
  final APIError? error;

  APIResponse({this.statusCode, this.data, this.error});

  factory APIResponse.fromJson(
    Map<String, dynamic> json,
    T Function(Object? json) fromJsonT,
  ) => _$APIResponseFromJson(json, fromJsonT);

  Map<String, dynamic> toJson(
    Object? Function(T value) toJsonT,
  ) => _$APIResponseToJson(this, toJsonT);
}
  1. 运行代码生成命令:
dart run build_runner build
  1. 使用示例:
  • 单个对象:APIResponse<User>.fromJson(json, User.fromJson)
  • 列表对象:APIResponse<List<User>>.fromJson(json, (json) => (json as List).map(User.fromJson).toList())

Dio 内置转换器(配合网络请求使用)

如果使用Dio做网络请求,可自定义响应转换器,直接在请求时指定泛型类型完成自动解码:

class CustomJsonConverter extends JsonConverter {
  @override
  T? convert<T>(dynamic input) {
    if (input is Map<String, dynamic> && T == APIResponse<User>) {
      return APIResponse<User>.fromJson(input, User.fromJson) as T;
    } else if (input is Map<String, dynamic> && T == APIResponse<List<User>>) {
      return APIResponse<List<User>>.fromJson(
        input,
        (data) => (data as List).map(User.fromJson).toList(),
      ) as T;
    }
    return super.convert(input);
  }
}

配置Dio并发起请求:

final dio = Dio()..converter = CustomJsonConverter();
final response = await dio.get<APIResponse<List<User>>>('/users');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 14:48:10