Flutter开发-03-JSON和序列化复杂数据分类封装基类

462 阅读5分钟

系列文章目录

Flutter开发-01-简单JSON数据和序列化在Flutter中的最佳实践

Flutter开发-02-复杂JSON数据和序列化在Flutter中的最佳实践


前言

上一篇文章记录了序列化解析复杂结构JSON数据的几个方法。其中使用泛型去解析的方法,大多情况会遇到两种带有数组列表类型的JSON数据结构,一个是 data 字段直接返回数组List,另一种是 data 字段返回的是一个分页的Bean对象,这个Bean对象中会有一个类似 datas 的字段返回数组List; 上文有好几种解析这两种JSON数据的方法。

1、使用泛型基类BaseResponse去解析,整个序列化非相同部分字段;

var userListBean = UserList.fromJson(userListMap);

2、JSON格式为返回List,使用 BaseResponse<List<User>> 去解析数据,这种方法注意一定要使用 List<dynamic> 进行强转,否则会报类型错误:

// 列表转换时一定要加一下强转List<dynamic>,否则会报错:type 'List<dynamic>' is not a subtype of type 'List<User>'
BaseResponse<List<User>> userListMap2 = BaseResponse.fromJson(userListMap,
    (json) => (json as List<dynamic>)
    // .map((e) => User.fromJson(e as Map<String, dynamic>))
    .map((e) => User.fromJson(e))
    .toList());

    var usersMap = userListMap2.data;

3、解析带有分页类型数组的JSON数据,一种方法是使用泛型创建BaseResponse基类+data字段对应的序列化Bean对象:

BaseResponse<UserPageBean> userListMap3 =
        BaseResponse.fromJson(json2Map, (json) => UserPageBean.fromJson(json));

var datas = userListMap3.data?.datas;

4、解析带有分页类型数组的JSON数据,另一种方法是使用泛型创建BaseResponse基类+data字段对应的序列化泛型类对象BaseList:

BaseResponse<BaseList<User>> userListMap3 = BaseResponse.fromJson(json2Map,
        (json) => BaseList.fromJson(json, (json) => User.fromJson(json)));

BaseList<User>? baseUsers = userListMap3.data;
// var datas = baseUsers?.datas;
List<User>? usersMap3 = baseUsers?.datas;

虽然一个 BaseResponse 解决了两种数据结构,但使用时的代码会有些复杂,很容易出错。可以对这三种类型的接口进行单独处理,简化操作。


一、封装基类BaseResponse

适用于 data 字段返回统一的数据结构(最为推荐吧),直接把 data 字段返回的JSON数据进行序列化操作;

核心代码:

import 'package:json_annotation/json_annotation.dart';

part 'base_response.g.dart';

/// 一个注释,用于代码生成器,使其知道该类需要生成JSON序列化逻辑
@JsonSerializable(genericArgumentFactories: true)
class BaseResponse<T> {
  //消息(例如成功消息文字/错误消息文字)
  String? message;

  bool? success = false;

  //自定义code(可根据内部定义方式)
  int? code;

  //接口返回的数据
  T? data;

  BaseResponse({
    this.message,
    this.success,
    this.code,
    this.data,
  });

  /// 从映射创建新BaseResponse实例所需的工厂构造函数。将映射传递给生成的' _BaseResponseFromJson() '构造函数。
  /// 构造函数以源类命名,在本例中为BaseResponse。
  factory BaseResponse.fromJson(
          Map<String, dynamic> json, T Function(dynamic json) fromJsonT) =>
      _$BaseResponseFromJson<T>(json, fromJsonT);

  /// ' toJson '是类声明支持序列化为JSON的约定。该实现仅仅调用私有的、生成的助手方法' _BaseResponseToJson '。
  Map<String, dynamic> toJson(Object? Function(T value) toJsonT) =>
      _$BaseResponseToJson<T>(this, toJsonT);
}

二、封装BaseListResponse基类

对于返回的JSON数据中 data 字段返回的是数组List的情形,可以直接使用泛型基类BaseListResponse去解析:

JOSN数据:

{
  "code":0,
  "message":"Success",
  "data":[
    {
      "name":"Jerry",
      "email":"Jerry@example.com"
    },
    {
      "name":"Alex",
      "email":"Alex@example.com"
    },
    {
      "name":"Tom",
      "email":"Tom@example.com"
    },
    {
      "name":"Jack",
      "email":"Jack@example.com"
    },
    {
      "name":"Lucy",
      "email":"Lucy@example.com"
    }
  ]
}

核心代码:

import 'package:json_annotation/json_annotation.dart';

part 'base_list_response.g.dart';

@JsonSerializable(genericArgumentFactories: true)
class BaseListResponse<T> {
  List<T> data;
  int code;
  String message;

  BaseListResponse(this.data, this.code, this.message);

  factory BaseListResponse.fromJson(
          Map<String, dynamic> json, T Function(dynamic json) fromJsonT) =>
      _$BaseListResponseFromJson<T>(json, fromJsonT);

  Map<String, dynamic> toJson(Object? Function(T value) toJsonT) =>
      _$BaseListResponseToJson<T>(this, toJsonT);
}

数据解析代码:

  Future decodeAssetJson() async {
    // 解析本地数据-List数组
    Map<String, dynamic> userListMap =
        await loadJsonAssets("assets/user_list.json");
  
  // 使用泛型基类BaseListResponse<T>去解析返回List数组类型的JOSN数据
    BaseListResponse<User> result =
        BaseListResponse.fromJson(userListMap, (json) => User.fromJson(json));

    List<User> list = result.data;

    if (kDebugMode) {
      print('\n ===============BaseListResponse解析数组开始================== \n ');

      print('result : $result');
      print('result : ${result.toString()}');

      if (list.isNotEmpty) {
        for (int i = 0; i < (list.length); i++) {
          print('result list $i : ${list[i].name}');
          print('result list $i : ${list[i].email}');
        }
      }

      print('\n ===============BaseListResponse解析数组结束================== \n ');
    }
}

输出日志:

result : Instance of 'BaseListResponse<User>'
result toString : Instance of 'BaseListResponse<User>'
result list 0 : Jerry
result list 0 : Jerry@example.com

...

三、封装BaseListResponse基类

对于返回的JSON数据中 data 字段返回的是数组List的情形,可以直接使用泛型基类BaseListResponse去解析:

JSON数据:

{
  "data":{
    "curPage":1,
    "datas":[
      {
        "name":"肖战",
        "email":"肖战@example.com"
      },
      {
        "name":"丁程鑫",
        "email":"丁程鑫@example.com"
      },
      {
        "name":"贺峻霖",
        "email":"贺峻霖@example.com"
      },
      {
        "name":"李天泽",
        "email":"李天泽@example.com"
      },
      {
        "name":"刘耀文",
        "email":"刘耀文@example.com"
      },
      {
        "name":"成毅",
        "email":"成毅@example.com"
      }
    ],
    "offset":0,
    "over":false,
    "pageCount":3,
    "size":20,
    "total":46
  },
  "code":0,
  "message":"Success get"
}

数据解析代码:

Future decodeAssetJson() async {
	// 解析本地数据-List数组
    Map<String, dynamic> json2Map =
        await loadJsonAssets("assets/user_list2.json");
    
    // 使用泛型基类BasePageListResponse<T> + PageList<T>去解析返回分页List数组类型的JOSN数据
    BasePageListResponse<User> result2 =
        BasePageListResponse.fromJson(json2Map, (json) => User.fromJson(json));

    var data = result2.data;
    // PageList<User> data = result2.data;
    var list2 = data.datas;
    // List<User> list2 = data.datas;

    if (kDebugMode) {
      print('\n ===============BasePageListResponse解析数组开始================== \n ');

      print('result : $result2');
      print('result toString : ${result2.toString()}');
      print('result code : ${result2.code}');
      print('result message : ${result2.message}');
      print('result data : ${result2.data}');
      print('result data curPage: ${result2.data.curPage}');
      print('result datas : ${result2.data.datas}');
      print('result datas jsonEncode: ${jsonEncode(result2.data.datas)}');

      if (list2.isNotEmpty) {
        for (int i = 0; i < (list2.length); i++) {
          print('result list $i : ${list2[i].name}');
          print('result list $i : ${list2[i].email}');
        }
      }

      print('\n ===============BasePageListResponse解析数组结束================== \n ');
    }
}

输出日志:

result : Instance of 'BasePageListResponse<User>'
result toString : Instance of 'BasePageListResponse<User>'
result code : 0
result message : Success get
result data : Instance of 'PageList<User>'
result datas : [Instance of 'User', Instance of 'User', Instance of 'User', Instance of 'User', Instance of 'User', Instance of 'User']
result data curPage: 10
result datas jsonEncode: [{"name":"肖战","email":"肖战@example.com"},{"name":"丁程鑫","email":"丁程鑫@example.com"},{"name":"贺峻霖","email":"贺峻霖@example.com"},{"name":"李天泽","email":"李天泽@example.com"},{"name":"刘耀文","email":"刘耀文@example.com"},{"name":"成毅","email":"成毅@example.com"}]
result list 0 : 肖战
result list 0 : 肖战@example.com

...


总结

提示:这里对文章进行总结:

以上两种JSON数据序列化后的数据解析代码就很统一了:

返回数组List的JSON解析:

// 使用泛型基类BaseListResponse<T>去解析返回List数组类型的JOSN数据
BaseListResponse<User> result =
   BaseListResponse.fromJson(userListMap, (json) => User.fromJson(json));

List<User> list = result.data;

返回分页数组列表的JSON解析:

// 使用泛型基类BasePageListResponse<T> + PageList<T>去解析返回分页List数组类型的JOSN数据
BasePageListResponse<User> result2 =
   BasePageListResponse.fromJson(json2Map, (json) => User.fromJson(json));

var data = result2.data;
// PageList<User> data = result2.data;
var list2 = data.datas;
// List<User> list2 = data.datas;

以上三种方法我觉得都很简洁,但是最好统一使用方法最好。即全部使用 BaseResponse<T> 只对 data 返回的字段进行序列化操作,然后进行数据解析; 或者 1、使用 BaseResponse<T> 解析一般结构的JSON数据,使用 BaseListResponse<T> 去解析返回数组List的JSON数据,只去序列化数组List中的Bean对象(比如本例中的 User); 2、使用 BasePageList<T> + PageList<T> 去解析返回 "分页" List的JSON数据,也是只去序列化数组List中的Bean对象(比如本例中的 User);