前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter Json渐进式解析(下)

Flutter Json渐进式解析(下)

作者头像
用户1907613
发布2019-05-07 17:25:35
2.5K0
发布2019-05-07 17:25:35
举报
文章被收录于专栏:Android群英传

JsonArray格式

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

代码语言:javascript
复制
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,代码如下所示。

代码语言:javascript
复制
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,代码如下所示。

代码语言:javascript
复制
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的时候喜欢使用工厂函数,例如下面的代码。

代码语言:javascript
复制
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的配置,如下所示。

代码语言:javascript
复制
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,如下所示。

代码语言:javascript
复制
1. {  
2.  "code": 0,  
3.  "result": "success",  
4.  "message": "message ok",  
5.  "data": {  
6.  "name": "xuyisheng",  
7.  "age": 18 
8.   }  
9. }  

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

代码语言:javascript
复制
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.}  

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

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

代码语言:javascript
复制
1. import 'package:json_annotation/json_annotation.dart';  
2.  
3. part 'TestJsonSerializable.g.dart';  

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

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

代码语言:javascript
复制
1. @JsonSerializable()  

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

代码语言:javascript
复制
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』。

全部的代码如下所示。

代码语言:javascript
复制
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来生成所需要的代码,命令如下所示。

代码语言:javascript
复制
1. ➜  flutter_json flutter packages pub run build_runner build  

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

代码语言:javascript
复制
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』的文件。打开该文件,如下所示。

代码语言:javascript
复制
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也支持对文件的监听,来自动创建新生成的数据实体类,指令如下所示。

代码语言:javascript
复制
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,并增加要自动插入的代码,如图所示。

模板代码示例如下所示。

代码语言:javascript
复制
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解析有一个比较完整和深入的理解,这样在使用这些工具的时候才能知其所以然。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 群英传 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JsonArray格式
  • 总结
  • 注意
  • json_serializable
    • 配置json_serializable
      • 示例
        • 进阶
          • 简化
            • 进一步简化
            • 真·总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档