使用 json_serializable 实现 Dart 泛型类的 JSON 序列化和反序列化

1,290 阅读4分钟

 在 Flutter 开发中,处理 HTTP 接口返回的 JSON 数据非常常见。通常我们会将 JSON 数据转换为 Dart 对象,以便于操作数据。然而,当我们尝试为泛型类实现 JSON 序列化和反序列化时,可能会遇到一些问题。本文将以一个 ApiResponse 泛型类为例,展示如何使用 json_serializable 包来处理泛型类的 JSON 序列化和反序列化问题。

问题描述

我们创建了一个 ApiResponse 泛型类,用于封装接口返回的结果:

class ApiResponse<T> {
  int? code;
  String? message;
  T? data;

  ApiResponse();

  factory ApiResponse.fromJson(Map<String, dynamic> json) => $ApiResponseFromJson<T>(json);

  Map<String, dynamic> toJson() => $ApiResponseToJson(this);

  @override
  String toString() {
    return jsonEncode(this);
  }
}

但是在编译时,出现了以下错误: 

The method '$ApiResponseFromJson' isn't defined for the type 'ApiResponse'.

 这个错误提示 $ApiResponseFromJson 方法没有定义。其实,这些方法通常是通过 json_serializable 自动生成的。为了处理 Dart 泛型类的 JSON 序列化和反序列化,我们需要使用 json_serializable 包并对代码进行一些调整。

解决思路

Dart 中的 JSON 序列化/反序列化可以通过手写或自动生成两种方式来实现。手写适合简单类,但对于复杂类或泛型类,json_serializable 提供了更优雅的解决方案。通过使用 json_serializable 包,我们可以自动生成序列化和反序列化的方法,同时支持泛型的转换。

解决方案

1. 添加 json_serializablebuild_runner 依赖

pubspec.yaml 文件中添加以下依赖:

dependencies:
  json_annotation: ^4.8.0

dev_dependencies:
  build_runner: ^2.4.0
  json_serializable: ^6.6.0

json_annotation 提供注解,用于标记需要序列化的类;build_runner 是代码生成工具,json_serializable 则是生成 JSON 序列化代码的核心。 

2. 修改 ApiResponse

为了让 json_serializable 生成合适的 JSON 序列化/反序列化方法,需要在类中添加 @JsonSerializable 注解,并且对 fromJsontoJson 方法做一些修改,支持泛型的转换。

import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';

part 'api_response.g.dart'; // 自动生成的文件名

@JsonSerializable(genericArgumentFactories: true) // 使用泛型的注解
class ApiResponse<T> {
  int? code;
  String? message;
  T? data;

  ApiResponse();

  // 使用 json_serializable 自动生成的方法,添加泛型转换
  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);

  @override
  String toString() {
    return jsonEncode(this);
  }
}

代码解析

  • @JsonSerializable(genericArgumentFactories: true):这一行指定生成的代码要支持泛型参数的工厂方法。
  • fromJsontoJson 方法中的 fromJsonTtoJsonT 是函数参数,用于指定如何将泛型类型 T 转换为 JSON 或从 JSON 转换回来。

3. 生成代码

在终端中运行以下命令,生成 api_response.g.dart 文件:

flutter pub run build_runner build

生成的 api_response.g.dart 文件中包含了 _ApiResponseFromJson_ApiResponseToJson 两个方法。这些方法根据 json_serializable 的注解自动生成,用于将 JSON 数据映射到 Dart 对象。 

4. 使用示例

以下是如何使用这个泛型类的例子:

void main() {
  // 模拟一个 JSON 数据
  var json = {
    "code": 200,
    "message": "Success",
    "data": {"name": "John Doe"}
  };

  // 将 JSON 解析为 ApiResponse 对象
  var response = ApiResponse<Map<String, dynamic>>.fromJson(
      json, (json) => json as Map<String, dynamic>);

  print(response.data); // 输出: {name: John Doe}

  // 将 ApiResponse 对象转换为 JSON
  var jsonResponse = response.toJson((data) => data);
  print(jsonResponse); // 输出: {code: 200, message: Success, data: {name: John Doe}}
}

解析

在这个示例中,我们定义了一个 ApiResponse<Map<String, dynamic>> 泛型类型,用于解析 JSON。通过指定 fromJson 函数 (json) => json as Map<String, dynamic> 来告诉解析器如何将 data 字段中的数据转换为 Map<String, dynamic> 类型。

toJson 方法的参数 (data) => data 用于将泛型 T 类型的数据转换为 JSON。

总结

使用 json_serializable 处理 Dart 泛型类的 JSON 序列化和反序列化能够简化代码,同时避免手动编写序列化方法。关键步骤包括:

  1. 添加依赖 json_serializablebuild_runner
  2. 使用 @JsonSerializable(genericArgumentFactories: true) 注解支持泛型。
  3. 在终端运行 build_runner 自动生成序列化代码。

通过这些步骤,我们可以更灵活地操作接口数据并封装泛型响应结构,极大地提升了代码的可读性和复用性。


这篇文章帮助大家理解如何在 Flutter 中使用 json_serializable 处理泛型类的 JSON 序列化问题,并提供了完整的代码示例和详细的解释。希望对大家有所帮助!