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(代码生成式解码)
通过代码生成自动生成序列化逻辑,配合泛型参数工厂,可轻松适配单个和列表类型的响应。
- 添加依赖到
pubspec.yaml:
dependencies: json_annotation: ^4.8.1 dev_dependencies: build_runner: ^2.4.4 json_serializable: ^6.7.0
- 为
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); }
- 运行代码生成命令:
dart run build_runner build
- 使用示例:
- 单个对象:
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
相关产品推荐
相关产品推荐

