前面几种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转换的一般步骤。
有些开发者在创建数据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. }
这种方式与本文所采用的方式并没有什么区别,只不过工厂函数可以更加灵活的控制实例的产生方式,所以在某些情况下,工厂函数会更加灵活,不过大部分情况下,使用普通的具名函数来创建实例就已经够了。
相比Android中的Json解析,Flutter的解析解析显得有些原始,原因在于Flutter不支持反射,所以无法像Gson那样通过反射来生成Json对象。
不过,回过头来想想在Flutter中的Json解析步骤,首先,需要把Json格式的字符串抽象成数据实体Model,这和在Android中使用Gson的步骤是一样的,只不过在Flutter中,多了一步生成fromJson函数的过程,而这个函数是非常简单的硬编码,即手动解析每个需要的字段,所以,这个过程也是可以通过脚本来自动化完成的,Flutter的开发团队也意识到了这一点,所以开发了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解析有一个比较完整和深入的理解,这样在使用这些工具的时候才能知其所以然。