Flutter使用json_serializable把json转换为model并使用泛型T封装BaseResponseModel

前言

我是使用json_serializable这个插件进行json序列化的。

因为服务器返回的json结构都是统一如下结构:

{
	"code": 200,
	"message": "SUCCESS",
	"data": {
		"countdown": 3,
		"createDate": 1625647940000,
		"modifiedDate": 1625647940000
	}
}

自然就会想通过一个泛型的方式,进行封装,进而不用每个json序列化model都包含code、message的通用部分。

当我直接把data通过泛型抽取出来时,执行如下命令生成代码时,出现如下错误:

import 'package:json_annotation/json_annotation.dart';
part 'response_model.g.dart';

@JsonSerializable(explicitToJson: true)
class ResponseModel<T> {
  T? data;
  int? code;
  String? message;
  ResponseModel({
    this.data,
    this.code,
    this.message
  });

  factory ResponseModel.fromJson(Map<String, dynamic> json) => _$ResponseModelFromJson<T>(json);
  Map<String, dynamic> toJson() => _$ResponseModelToJson(this);
}

错误:

flutter packages pub run build_runner build 

[INFO] Generating build script...
[INFO] Generating build script completed, took 515ms

[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 61ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 511ms

[INFO] Running build...
[INFO] 1.3s elapsed, 10/14 actions completed.
[SEVERE] json_serializable:json_serializable on lib/model/response_model.dart:

Could not generate `fromJson` code for `data` because of type `T` (type parameter).
To support type parameters (generic types) you can:
* Use `JsonConverter`
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonConverter-class.html
* Use `JsonKey` fields `fromJson` and `toJson`
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/fromJson.html
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonKey/toJson.html
* Set `JsonSerializable.genericArgumentFactories` to `true`
  https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/genericArgumentFactories.html
package:doucan_flutter/model/response_model.dart:6:6
  ╷
6 │   T? data;
  │      ^^^^
  ╵
[INFO] Running build completed, took 1.6s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 61ms

[SEVERE] Failed after 1.6s
pub finished with exit code 1

主要问题就是这里:
Could not generate `fromJson` code for `data` because of type `T` (type parameter).

在GitHub的issue中找到如下答案:
https://github.com/google/json_serializable.dart/issues/252
Flutter使用json_serializable把json转换为model并使用泛型T封装BaseResponseModel_第1张图片
依葫芦画瓢改了一下,木有成功,可能我理解不到位。

于是继续Google…

发现了这篇文章:
https://wamae.medium.com/generics-and-json-serialization-in-flutter-a8d335840d7b

按照里面的写法成功了一大半,但还是有些错误,可能和flutter版本有关,我使用的是Flutter 2.2.3 Dart 2.13.4

做些调整后就成功了!!~

因为具体Data类的fromJson接收的参数类型为Map json,而原文中的是Object,导致调用时失败。以下为修改内容:

//原文:
factory BaseResponse.fromJson(
      Map json,
      T Function(Object json) fromJsonT,
      ) =>
      _$BaseResponseFromJson(json, fromJsonT);

// 改为:
factory BaseResponse.fromJson(
      Map json,
      T Function(Map json) fromJsonT
      ) =>
      _$BaseResponseFromJson(json, fromJsonT);

//相对应的,base_response.g.dart文件也需要修改FromJson方法中的参数变量类型

以下是最终的代码:

## file : response_model.dart

import 'package:json_annotation/json_annotation.dart';

part 'response_model.g.dart';

@JsonSerializable(
    genericArgumentFactories: true, fieldRename: FieldRename.snake)
class ResponseModel<T> {
  @JsonKey(name: 'code')
  final int code;
  @JsonKey(name: 'message')
  final String message;
  @JsonKey(name: 'data')
  final T data;

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

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

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

## file : response_model.g.dart

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'response_model.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

ResponseModel<T> _$ResponseModelFromJson<T>(
  Map<String, dynamic> json,
  T Function(Map<String, dynamic> json) fromJsonT,
) {
  return ResponseModel<T>(
    fromJsonT(json['data']),
    json['code'] as int,
    json['message'] as String,
  );
}

Map<String, dynamic> _$ResponseModelToJson<T>(
  ResponseModel<T> instance,
  Object? Function(T value) toJsonT,
) =>
    <String, dynamic>{
      'code': instance.code,
      'message': instance.message,
      'data': toJsonT(instance.data),
    };

开始食用:

data具体类:

import 'package:json_annotation/json_annotation.dart';

part 'splash_model.g.dart';

@JsonSerializable(explicitToJson: true)
class SplashmodelData {
  int countdown;
  int createDate;
  int modifiedDate;
 
  SplashmodelData(
    this.countdown,
    this.createDate,
    this.modifiedDate,
  );

  factory SplashmodelData.fromJson(Map<String, dynamic> json) =>
      _$SplashmodelDataFromJson(json);

  Map<String, dynamic> toJson() => _$SplashmodelDataToJson(this);
}

import 'dart:convert';

import 'package:doucan_flutter/config/network_config.dart';
import 'package:doucan_flutter/model/response_model.dart';
import 'package:doucan_flutter/model/splash_model.dart';
import 'package:http/http.dart' as http;

class SplashDao {
  static Future<SplashmodelData?> fetch() async {
    Uri url = Uri.parse(NetworkConfig.url('app_launcher/patient/splash'));
    final respones = await http.get(url);
    print(respones.statusCode);
    print(respones.body);
    if (respones.statusCode == 200) {
      var jsonResult = json.decode(Utf8Decoder().convert(respones.bodyBytes));
      return ResponseModel<SplashmodelData>.fromJson(
          jsonResult, (data) => SplashmodelData.fromJson(data)).data;
    } else {
      throw Exception(
          'request app_launcher/patient/splash failed ');
    }
  }
}

你可能感兴趣的:(Flutter仓颉之旅,flutter,json)