Flutter Json渐进式解析(下)

JsonArray格式

前面几种Json数据格式最外层都是JsonObject,而下面这种,最外层就是一个JsonArray,数据如下所示。

1.  [  

2.    {  

3.      "name": "xuyisheng",  

4.      "age": 18,  

5.      "sex": "M"  

6.    },  

7.    {  

8.      "name": "zhujia",  

9.      "age": 3,  

10.    "sex": "F"  

11.  }  

12.]  

对于这样的Json解析,可以参考下Android中的Json解析,首先,可以在最外面封装一个数据Model,其属性就是一个包含上面数据结构的List,首先,还是创建里层的数据Model,代码如下所示。

1.  class Family {  

2.    String name;  

3.    String sex;  

4.    int age;  

5.    

6.    Family.fromJson(Map<String, dynamic> json)  

7.        : name = json['name'],  

8.          sex = json['sex'],  

9.          age = json['age'];  

10.  

11.  @override  

12.  String toString() {  

13.    return 'name: $name ,age: $age ,sex: $sex';  

14.  }  

15.}  

接下来,创建外层的数据Model,代码如下所示。

1. class BasicList {  
2.   List<Family> families;  
3.  
4.   BasicList.fromJson(List<dynamic> json)  
5.       : families = json.map((i) => Family.fromJson(i)).toList();  
6.  
7.  @override 
8.   String toString() {  
9.  return 'BasicList: $families';  
10.  }  
11.}  

这里要注意的是,构造函数接受的参数为List而非之前的Map,原因就是这种结构的Json通过dart:convert转换出来的是List<dynamic>。

总结

上面列举的Json类型基本上已经涵盖了平常开发中所遇到的Json数据格式,总结下Dart中进行Json转换的一般步骤。

  1. 确定最外层返回的是List还是Map
  2. 从最里层开始向外创建数据Model
  3. 对特定类型的数据进行数据类型转换

注意

有些开发者在创建数据Model的时候喜欢使用工厂函数,例如下面的代码。

1. BasicMapModel({this.code, this.result, this.message});  
2.  
3. factory BasicMapModel.fromJson(Map<String, dynamic> json) {  
4.  return BasicMapWithListModel(  
5.     code: json['code'],  
6.     result: json['result'],  
7.     message: json['message'],  
8.   );  
9. }  

这种方式与本文所采用的方式并没有什么区别,只不过工厂函数可以更加灵活的控制实例的产生方式,所以在某些情况下,工厂函数会更加灵活,不过大部分情况下,使用普通的具名函数来创建实例就已经够了。

json_serializable

相比Android中的Json解析,Flutter的解析解析显得有些原始,原因在于Flutter不支持反射,所以无法像Gson那样通过反射来生成Json对象。

不过,回过头来想想在Flutter中的Json解析步骤,首先,需要把Json格式的字符串抽象成数据实体Model,这和在Android中使用Gson的步骤是一样的,只不过在Flutter中,多了一步生成fromJson函数的过程,而这个函数是非常简单的硬编码,即手动解析每个需要的字段,所以,这个过程也是可以通过脚本来自动化完成的,Flutter的开发团队也意识到了这一点,所以开发了json_serializable来帮助开发者完成这些繁杂的体力劳动。

配置json_serializable

首先,打开pubspec.yaml文件,增加json_annotation、build_runner和json_serializable的配置,如下所示。

1. dependencies:  
2.   flutter:  
3.     sdk: flutter  
4.  
5.   json_annotation: ^2.2.0 
6.  
7. dev_dependencies:  
8.   flutter_test:  
9.     sdk: flutter  
10. 
11.  build_runner: ^1.3.3 
12.  json_serializable: ^2.2.1 

这里要注意的是,build_runner和json_serializable是放在dev_dependencies中的,它类似于Android中的debugCompile,也就是只在编译的时候使用,Release中是不会有这两个库的,它们仅仅是帮助开发者来生成代码。

这些库的最新版本,可以从DartPub上查找,地址如下所示。

json_serializable

build_runner

json_annotation

配置好之后,点击AndroidStudio上的Packages get、Packages upgrade或者在命令行中来执行这些指令来获取这些引用库。

示例

下面就通过一个例子来演示下如何使用json_serializable,首先,找到一个之前使用的Json,如下所示。

1. {  
2.  "code": 0,  
3.  "result": "success",  
4.  "message": "message ok",  
5.  "data": {  
6.  "name": "xuyisheng",  
7.  "age": 18 
8.   }  
9. }  

这个Json,前面已经设置好了数据实体,如下所示。

1. class BasicMapWithModel {  
2.  int code;  
3.   String result;  
4.   String message;  
5.   Data data;  
6. }  
7.  
8. class Data {  
9.   String name;  
10. int age;  
11.}  

接下来,为了使用这个库,还需要给这个类添加一些代码,分为下面几个步骤。

首先,引入相关的依赖,代码如下所示。

1. import 'package:json_annotation/json_annotation.dart';  
2.  
3. part 'TestJsonSerializable.g.dart';  

TestJsonSerializable.g.dart文件就是稍后需要自动生成的文件,它的命名方式就是『数据实体的文件名.g.dart』。

接下来,给每个实体类增加注解,build_runner就是通过这个注解来寻找需要生成代码的类,如下所示。

1. @JsonSerializable()  

然后给每个实体增加构造函数、fromJson和toJson函数,这里要注意的是,fromJson和toJson函数不需要具体实现,只需要生成函数名即可,具体的手动解析Json的过程,就是build_runner来生成的,代码如下所示。

1. TestJsonSerializable(this.code, this.result, this.message, this.data);  
2.  
3. factory TestJsonSerializable.fromJson(Map<String, dynamic> srcJson) =>  
4.     _$TestJsonSerializableFromJson(srcJson);  
5.  
6. Map<String, dynamic> toJson() => _$TestJsonSerializableToJson(this); 

以最外层的Model为例,这里生成文件名的规则是『_$数据实体类名FromJson』、『_$数据实体类名ToJson』。

全部的代码如下所示。

1. import 'package:json_annotation/json_annotation.dart';  
2.  
3. part 'TestJsonSerializable.g.dart';  
4.  
5. @JsonSerializable()  
6. class TestJsonSerializable extends Object {  
7.  int code;  
8.   String result;  
9.   String message;  
10.  Data data;  
11. 
12.  TestJsonSerializable(this.code, this.result, this.message, this.data);  
13. 
14.  factory TestJsonSerializable.fromJson(Map<String, dynamic> srcJson) =>  
15.      _$TestJsonSerializableFromJson(srcJson);  
16. 
17.  Map<String, dynamic> toJson() => _$TestJsonSerializableToJson(this);  
18.}  
19. 
20.@JsonSerializable()  
21.class Data extends Object {  
22.  String name;  
23. int age;  
24. 
25.  Data(this.name, this.age);  
26. 
27.  factory Data.fromJson(Map<String, dynamic> srcJson) =>  
28.      _$DataFromJson(srcJson);  
29. 
30.  Map<String, dynamic> toJson() => _$DataToJson(this);  
31.}  

在编写这个代码的时候,由于需要的代码还没生成,所以编译器会报红警告,这是正常的,当代码生成后,这些警告自然就没有了。

最后,通过运行build_runner来生成所需要的代码,命令如下所示。

1. ➜  flutter_json flutter packages pub run build_runner build  

在项目目录下执行上面的指令即可,生成过程如下图所示。

1. ➜  flutter_json flutter packages pub run build_runner build  
2. [INFO] Generating build script...  
3. [INFO] Generating build script completed, took 423ms  
4.  
5. [INFO] Initializing inputs  
6. [INFO] Reading cached asset graph...  
7. [INFO] Reading cached asset graph completed, took 127ms  
8.  
9. [INFO] Checking for updates since last build...  
10.[INFO] Checking for updates since last build completed, took 977ms  
11. 
12.[INFO] Running build...  
13.[INFO] 1.0s elapsed, 0/1 actions completed.  
14.[INFO] Running build completed, took 1.1s  
15. 
16.[INFO] Caching finalized dependency graph...  
17.[INFO] Caching finalized dependency graph completed, took 61ms  
18. 
19.[INFO] Succeeded after 1.1s with 2 outputs (3 actions)  

指令执行成功后,就会在实体类的当前目录下生成前面定义的『.g.dart』的文件。打开该文件,如下所示。

1. // GENERATED CODE - DO NOT MODIFY BY HAND 
2.  
3. part of 'TestJsonSerializable.dart';  
4.  
5. // *********************************************************************** 
6. // JsonSerializableGenerator 
7. // *********************************************************************** 
8.  
9. TestJsonSerializable _$TestJsonSerializableFromJson(Map<String, dynamic> json) {  
10. return TestJsonSerializable(  
11.      json['code'] as int,  
12.      json['result'] as String,  
13.      json['message'] as String,  
14.      json['data'] == null 
15.          ? null 
16.          : Data.fromJson(json['data'] as Map<String, dynamic>));  
17.}  
18. 
19.Map<String, dynamic> _$TestJsonSerializableToJson(  
20.        TestJsonSerializable instance) =>  
21.    <String, dynamic>{  
22. 'code': instance.code,  
23. 'result': instance.result,  
24. 'message': instance.message,  
25. 'data': instance.data  
26.    };  
27. 
28.Data _$DataFromJson(Map<String, dynamic> json) {  
29. return Data(json['name'] as String, json['age'] as int);  
30.}  
31. 
32.Map<String, dynamic> _$DataToJson(Data instance) =>  
33.    <String, dynamic>{'name': instance.name, 'age': instance.age};  

这些代码就是build_runner自动生成的解析Json的代码,至此,解析所需要的实体类就创建好了,现在回过头来看看,与之前手动解析Json自己写的那些方法,基本都是一样的,只不过这些机械的代码被build_runner自动生成了而已。

另外,build_runner也支持对文件的监听,来自动创建新生成的数据实体类,指令如下所示。

1. flutter packages pub run build_runner watch  

进阶

json_annotation是类似Gson的注解,除了前面提到的@JsonSerializable()注解,其实还有很多其它注解,例如:@JsonKey(name="Json_Name"),即获取指定Json名的字段,类似Gson中的@SerializedName("Json_Name ")。更多的参数可以在json_annotation的Github上找到,这里不进行进一步的解释了,都是一些配置参数。

简化

虽然说官方提供了json_annotation、build_runner和json_serializable来简化Json解析实体类的生成,但是这个使用过程还是非常繁琐的,特别是创建好最简单的实体类后,还需要补充各种依赖、fromJson函数等内容,其实还是非常不方便的,但好在这些过程实际上也是非常机械的,同样可以通过一些方式来进行简化,Android Studio的Live Templates就是一个不错的选择,创建一个Live Templates,并增加要自动插入的代码,如图所示。

模板代码示例如下所示。

1. import 'package:json_annotation/json_annotation.dart';  
2.  
3. part '$NAME$.g.dart';  
4.  
5. @JsonSerializable()  
6. class $NAME$ extends Object {  
7.  
8.     String $V1$;  
9.     String $V2$;  
10. 
11.    $NAME$(this.$V1$, this.$V2$);  
12. 
13.    factory $NAME$.fromJson(Map<String, dynamic> srcJson) =>  
14.    _$$$NAME$FromJson(srcJson);  
15. 
16.    Map<String, dynamic> toJson() => _$$$NAME$ToJson(this);  
17.}  

这就是一个最简单的数据实体类的模板代码了,在编辑器中输入创建的快捷键,就可以非常方便的使用这个快捷代码了,创建好之后,再通过build_runner就可以创建所需要的其它文件了。

这里需要注意的是,『$』在模板代码中表示的是变量,但这里需要将它作为文本处理,所以查阅idea的文档后发现,可以通过『$$』来进行转义,相关文档如下。

https://www.jetbrains.com/help/idea/template-variables.html

进一步简化

相比在Android中使用GsonFormat进行Json的实体类生成,Flutter中生成实体类的方式还是有些麻烦,因此,如果能够将GsonFormat的源码进行改造,实际上是完全可以直接通过Json生成实体类的。

当然,类似的工具还有很多,毕竟Json to Model在各个平台中都是需要的,类似的还有下面这个工具。

https://caijinglong.github.io/json2dart/index_ch.html

这些工具都可以完成这样的操作。

真·总结

本文从最基础的FlutterJson解析到一步步更加复杂的Json解析,再到更加高效的Json解析,一点点的让开发者了解如何在Flutter中处理Json。之所以没有直接讲解最高效的使用方法,是为了让开发者对Flutter中的Json解析有一个比较完整和深入的理解,这样在使用这些工具的时候才能知其所以然。

原文发布于微信公众号 - Android群英传(android_heroes)

原文发表时间:2019-04-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券