我是靠谱客的博主 紧张棉花糖,这篇文章主要介绍Flutter 使用 json_serializable 解析 JSON 支持泛型,现在分享给大家,希望可以做个参考。

一般情况下,服务端接口都会有一套数据结构规范,比如

复制代码
1
2
3
4
5
6
{ "items": [], "success": true, "msg": "" }

不同的接口,items 中返回的数据结构一般都是不一样的,这时使用泛型,可以简化代码

本文将以 wanAndroid 提供的开放 API 为例,介绍如何通过泛型类接解析 JSON 数据,简化代码。另外,对 wanAndroid 提供开放 API 的行为表示感谢。

本文解析 JSON 使用的方案,是官方推荐的 json_serializable,至于为什么选择 json_serializable,可以参考我之前写的一篇文章:Flutter 使用 json_serializable 解析 JSON 最佳方案

下面开始进入正文

使用 json_serializable 支持泛型

json_serializable 在大概两年前发布的 v3.5.0 版本开始支持泛型,只需要在 @JsonSerializable() 注解中设置 genericArgumentFactories 为 true,同时需要对 fromJson 和 toJson 方法进行调整,即可支持泛型解析,如下所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
@JsonSerializable(genericArgumentFactories: true) class Response<T> { int status; T value; factory Response.fromJson( Map<String, dynamic> json, T Function(dynamic json) fromJsonT, ) => _$ResponseFromJson<T>(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$ResponseToJson<T>(this, toJsonT); }

和正常实体类相比,fromJson 方法多了一个函数参数 T Function(dynamic json) fromJsonT;toJson 方法也多了一个函数参数:Object? Function(T value) toJsonT

分析数据结构

下面使用 wanAndroid 开放 API 接口数据,进行代码实践,我们先看一下服务端接口返回的数据结构

一般接口返回数据结构如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{ "data": [ { "desc": "一起来做个App吧", "id": 10, "imagePath": "https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png", "isVisible": 1, "order": 1, "title": "一起来做个App吧", "type": 1, "url": "https://www.wanandroid.com/blog/show/2" } ], "errorCode": 0, "errorMsg": "" }

带分页信息的列表接口,返回数据结构如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{ "data": { "curPage": 1, "datas": [ { "id": 23300, "link": "https://juejin.cn/post/7114142706557075487", "niceDate": "2022-06-28 15:30", "niceShareDate": "2022-06-28 15:30", "publishTime": 1656401449000, "realSuperChapterId": 493, "shareDate": 1656401449000, "shareUser": "灰尘", "superChapterId": 494, "superChapterName": "广场Tab", "title": "Flutter 使用 json_serializable 解析 JSON 最佳方案" } ], "offset": 0, "over": false, "pageCount": 3, "size": 20, "total": 46 }, "errorCode": 0, "errorMsg": "" }

通过上面的接口示例,我们可以发现,返回的数据结构有以下两种情况:

在一般情况下 data 是一个数组

复制代码
1
2
3
4
5
6
{ "data": [], "errorCode": 0, "errorMsg": "" }

在分页相关接口,data 是一个对象

复制代码
1
2
3
4
5
6
{ "data": {}, "errorCode": 0, "errorMsg": "" }

复杂方案

如果想定义一个模型类,同时处理上述两种情况,可以把整个 data 都定义为泛型,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import 'package:json_annotation/json_annotation.dart'; part 'base_response.g.dart'; @JsonSerializable(genericArgumentFactories: true) class BaseResponse<T> { T data; int errorCode; String errorMsg; BaseResponse({ required this.data, required this.errorCode, required this.errorMsg, }); factory BaseResponse.fromJson( Map<String, dynamic> json, T Function(dynamic json) fromJsonT, ) => _$BaseResponseFromJson<T>(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$BaseResponseToJson<T>(this, toJsonT); } @JsonSerializable(genericArgumentFactories: true) class ListData<T> { int? curPage; List<T> datas; int? offset; bool? over; int? pageCount; int? size; int? total; ListData({ this.curPage, required this.datas, this.offset, this.over, this.pageCount, this.size, this.total, }); factory ListData.fromJson( Map<String, dynamic> json, T Function(dynamic json) fromJsonT, ) => _$ListDataFromJson<T>(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$ListDataToJson<T>(this, toJsonT); }

测试代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void main() { test("json", () { String str = '{"data": [{"category": "设计","icon": "","id": 31,"link": "https://tool.gifhome.com/compress/","name": "gif压缩","order": 4444,"visible": 1}],"errorCode": 0,"errorMsg": ""}'; Map<String, dynamic> json = jsonDecode(str); BaseResponse<List<CategoryModel>> result = BaseResponse.fromJson(json, (json) { return (json as List<dynamic>) .map((e) => CategoryModel.fromJson(e as Map<String, dynamic>)) .toList(); }); List<CategoryModel> list = result.data; CategoryModel model = list[0]; print(model.toJson()); expect("category:设计", "category:${model.category}"); }); test("json list", () { String str = '{"data": {"curPage": 1,"datas": [{"id": 23300,"link": "https://juejin.cn/post/7114142706557075487","niceDate": "2022-06-28 15:30","niceShareDate": "2022-06-28 15:30","publishTime": 1656401449000,"realSuperChapterId": 493,"shareDate": 1656401449000,"shareUser": "灰尘","superChapterId": 494,"superChapterName": "广场Tab","title": "Flutter 使用 json_serializable 解析 JSON 最佳方案"}],"offset": 0,"over": false,"pageCount": 3,"size": 20,"total": 46},"errorCode": 0,"errorMsg": ""}'; Map<String, dynamic> json = jsonDecode(str); BaseResponse<ListData<ArticleModel>> result = BaseResponse.fromJson(json, (json) { return ListData.fromJson(json, (json) => ArticleModel.fromJson(json)); }); ListData<ArticleModel> listData = result.data; List<ArticleModel> datas = listData.datas; ArticleModel model = datas[0]; print(model.toJson()); expect("id:23300", "id:${model.id}"); }); }

虽然一个 BaseResponse 解决了两种数据结构,但使用时的代码会有些复杂,很容易出错。

一般接口:

复制代码
1
2
3
4
5
6
7
8
BaseResponse<List<CategoryModel>> result = BaseResponse.fromJson(json, (json) { return (json as List<dynamic>) .map((e) => CategoryModel.fromJson(e as Map<String, dynamic>)) .toList(); });

分页接口:

复制代码
1
2
3
4
5
6
BaseResponse<ListData<ArticleModel>> result = BaseResponse.fromJson(json, (json) { return ListData.fromJson(json, (json) => ArticleModel.fromJson(json)); });

简化方案

可以对一般接口和列表分页接口进行单独处理,

处理一般接口的泛型类,命名为 BaseCommonResponse,代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import 'package:json_annotation/json_annotation.dart'; part 'base_common_response.g.dart'; @JsonSerializable(genericArgumentFactories: true) class BaseCommonResponse<T> { List<T> data; int errorCode; String errorMsg; BaseCommonResponse({ required this.data, required this.errorCode, required this.errorMsg, }); factory BaseCommonResponse.fromJson( Map<String, dynamic> json, T Function(dynamic json) fromJsonT, ) => _$BaseCommonResponseFromJson<T>(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$BaseCommonResponseToJson<T>(this, toJsonT); }

处理分页列表接口的泛型类,命令为 BaseListResponse

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import 'package:json_annotation/json_annotation.dart'; part 'base_list_response.g.dart'; @JsonSerializable(genericArgumentFactories: true) class BaseListResponse<T> { ListData<T> data; int errorCode; String errorMsg; BaseListResponse({ required this.data, required this.errorCode, required this.errorMsg, }); 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); } @JsonSerializable(genericArgumentFactories: true) class ListData<T> { int? curPage; List<T> datas; int? offset; bool? over; int? pageCount; int? size; int? total; ListData({ this.curPage, required this.datas, this.offset, this.over, this.pageCount, this.size, this.total, }); factory ListData.fromJson( Map<String, dynamic> json, T Function(dynamic json) fromJsonT, ) => _$ListDataFromJson<T>(json, fromJsonT); Map<String, dynamic> toJson(Object? Function(T value) toJsonT) => _$ListDataToJson<T>(this, toJsonT); }

测试代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void main() { test("json", () { String str = '{"data": [{"category": "设计","icon": "","id": 31,"link": "https://tool.gifhome.com/compress/","name": "gif压缩","order": 4444,"visible": 1}],"errorCode": 0,"errorMsg": ""}'; Map<String, dynamic> json = jsonDecode(str); BaseCommonResponse<CategoryModel> result = BaseCommonResponse.fromJson( json, (json) => CategoryModel.fromJson(json)); List<CategoryModel> list = result.data; CategoryModel model = list[0]; print(model.toJson()); expect("category:设计", "category:${model.category}"); }); test("json list", () { String str = '{"data": {"curPage": 1,"datas": [{"id": 23300,"link": "https://juejin.cn/post/7114142706557075487","niceDate": "2022-06-28 15:30","niceShareDate": "2022-06-28 15:30","publishTime": 1656401449000,"realSuperChapterId": 493,"shareDate": 1656401449000,"shareUser": "灰尘","superChapterId": 494,"superChapterName": "广场Tab","title": "Flutter 使用 json_serializable 解析 JSON 最佳方案"}],"offset": 0,"over": false,"pageCount": 3,"size": 20,"total": 46},"errorCode": 0,"errorMsg": ""}'; Map<String, dynamic> json = jsonDecode(str); BaseListResponse<ArticleModel> result = BaseListResponse.fromJson(json, (json) => ArticleModel.fromJson(json)); ListData<ArticleModel> listData = result.data; List<ArticleModel> datas = listData.datas; ArticleModel model = datas[0]; print(model.toJson()); expect("id:23300", "id:${model.id}"); }); }

这时使用时的代码,就比较简单了,代码如下:

一般接口,使用 BaseCommonResponse

复制代码
1
2
3
4
BaseCommonResponse<CategoryModel> result = BaseCommonResponse.fromJson( json, (json) => CategoryModel.fromJson(json));

列表分页接口,使用 BaseListResponse

复制代码
1
2
3
4
BaseListResponse<ArticleModel> result = BaseListResponse.fromJson( json, (json) => ArticleModel.fromJson(json));

以上就是我在 Flutter 中解析 JSON 数据时处理泛型的实践经验。

最后

以上就是紧张棉花糖最近收集整理的关于Flutter 使用 json_serializable 解析 JSON 支持泛型的全部内容,更多相关Flutter内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(44)

评论列表共有 0 条评论

立即
投稿
返回
顶部