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

Dart/Flutter泛型T如何调用toJson实现通用POST数据提交

问题根因

Dart 泛型如果没有显式指定类型上限,编译器会默认泛型T的类型为Object?,无法确认传入的实例是否存在toJson()方法,因此直接调用会触发编译错误。

可落地的实现方案

方案1:泛型类型约束(轻量、类型安全,适配绝大多数场景)

先定义统一的序列化抽象接口,所有需要传入POST方法的请求实体都实现该接口,强制要求实现toJson()序列化方法:

abstract class JsonSerializable {
  Map<String, dynamic> toJson();
}

修改原有的postData方法,给泛型加上类型上限,明确告诉编译器所有传入的T类型都必然实现了JsonSerializable接口,一定存在可调用的toJson()方法:

Future<ReturnModel> postData<T extends JsonSerializable>(T model) async {
  final response = await http.post(
      Uri.parse('https://localhost/SomeAddress'),
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      // 此处不再报错,编译器已可识别T的toJson方法
      body: jsonEncode(model.toJson())
  );

  if (response.statusCode == StatusCode.OK) {
    return ReturnModel.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('请求发生错误');
  }
}

后续所有请求实体类只要实现JsonSerializable接口就可以正常传入方法,举个使用示例:

// 定义请求实体
class LoginRequest implements JsonSerializable {
  final String username;
  final String password;

  LoginRequest({required this.username, required this.password});

  @override
  Map<String, dynamic> toJson() {
    return {
      'username': username,
      'password': password,
    };
  }
}

// 调用请求
final result = await postData(LoginRequest(username: 'demo', password: '123456'));

方案2:外部传入序列化函数(灵活性更高)

如果不想给所有实体类统一加接口约束,可以给方法新增一个序列化函数参数,由调用方传入具体的JSON转换逻辑,不需要约束泛型类型:

Future<ReturnModel> postData<T>(T model, Map<String, dynamic> Function(T instance) toJson) async {
  final response = await http.post(
      Uri.parse('https://localhost/SomeAddress'),
      headers: <String, String>{
        'Content-Type': 'application/json; charset=UTF-8',
      },
      body: jsonEncode(toJson(model))
  );

  if (response.statusCode == StatusCode.OK) {
    return ReturnModel.fromJson(jsonDecode(response.body));
  } else {
    throw Exception('请求发生错误');
  }
}

调用时手动传入对应实体的序列化逻辑即可:

final result = await postData<LoginRequest>(
  LoginRequest(username: 'demo', password: '123456'),
  (instance) => instance.toJson()
);

这种方案不需要修改原有实体类的继承实现,适配第三方生成的实体类更方便,缺点是每次调用都需要传入转换函数,重复代码相对多一些。

实践建议

如果项目中网络请求场景较多,建议进一步封装网络请求基类,将基础域名、公共请求头、日志拦截、统一错误处理、响应解析等通用逻辑收拢,避免每次写请求都重复编写相同代码。后续如果使用json_serializable等自动化JSON序列化工具,上述两种方案都可以直接兼容,不需要修改核心请求逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:21:26