背景: 在开发中,服务端通常返回Json数据,我们需要将Json数据转模型对象来使用。一般情况下,我们会使用一些第三方库来动态转化Model,但是Flutter中没有像Java的Gson/Jackson这类Json序列化类库,因为Flutter中禁用运行时反射。官方解释是运行时反射会干扰Dart的
tree shaking
,使用tree shaking
可以在release版中去除未使用的代码,这可以显著优化应用程序的大小。由于反射会默认应用到所有代码,因此tree shaking
会很难工作,因为在启用反射时很难知道哪些代码未被使用,因此冗余代码很难剥离,所以Flutter中禁用了Dart的反射功能,而正因如此也就无法实现动态转化Model的功能。
在此基础上,接下来我们看下Flutter中还有哪几种Json转模型的方式:
一. 手动转化
在上篇[Flutter] 07-Flutter中反序列化Json已经通过6个示例分析过了, 这里不再讨论。
二. json_serializable
json_serializable是dart官方推荐和提供的JSON转Model的方式:
- 一个自动化源代码生成器来为你生成 JSON 序列化数据模板;
- 由于序列化数据代码不再需要手动编写或者维护,你可以将序列化 JSON 数据在运行时的异常风险降到最低;
第1步:添加相关的依赖
依赖分为项目依赖(dependencies),开发依赖(dev_dependencies),在pubspec.yaml
中添加如下依赖:
dependencies:
json_annotation:^3.0.1
dev_dependencies:
json_serializable:^3.2.5
build_runner:^1.8.0
- 注意:添加后需要执行
flutter pub get
确保我们的项目中有这些依赖。 - 注意:yaml配置文件对于缩进要求十分严格,下面的
build_runner
和json_serializable
应该是与flutter_test
平级的,千万不要写在flutter_test缩进后,这样它会认为这两个是flutter_test的子集目录!
由于很多朋友在这一步遇到了问题,这里贴出源码:
第2步:以json_serializable 的方式创建模型类
- 根据下面简单Json数据创建模型类:
final jsonInfo = {
"nickname": "coderTao",
"age": 20,
"courses": ["政治", "高数", "英语"],
"register_date": "2018-2-22",
"computer": {
"brand": "MackBook",
"price": 9999
}
};
- User类的代码:
// 1.import 导入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
import 'computer_model.dart';
// 2.user.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
part 'user_model.g.dart';
// 3.这个标注是告诉生成器,这个类是需要生成Model类的
@JsonSerializable()
class User {
String name;
int age;
//显式关联JSON字段名与Model属性的对应关系,
// 如下将属性registerDate和register_date字段关联
@JsonKey(name: "register_date")
String registerDate;
List courses;
Computer computer;
// 4.必须的构造方法
User(this.name, this.age, this.registerDate, this.courses, this.computer);
// 5.必须有的对应工厂构造器
factory User.fromJson(Map json) => _$UserFromJson(json);
Map toJson() => _$UserToJson(this);
//这里 toString方法不是必须的, 只是用测试数据
@override
String toString() {
return'User{name: $name, age: ${age}, registerDate: $registerDate, courses: $courses, computer: $computer}';
}
}
- Computer类的代码:
// 1.import 导入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
// 2.computer.g.dart 将在我们运行生成命令后json_serializable帮我们自动生成.g.dart文件,在未执行命令前该行可能会报错
part 'computer.g.dart';
// 3.这个标注告诉json_serializable哪一个类需要进行转换生成Model类
@JsonSerializable()
class Computer {
String brand;
double price;
//4.必须的构造方法
Computer(this.brand, this.price);
//5.必须有的对应工厂构造器
factory Computer.fromJson(Map json) => _$ComputerFromJson(json);
Map toJson() => _$ComputerToJson(this);
//这里 toString方法不是必须的, 只是用测试数据
@override
String toString() {
return'Computer{brand: $brand, price: $price}';
}
}
最后总结一下以json_serializable 的方式创建模型类必须5步:
- 1.import 导入
json_annotation.dart
。
import 'package:json_annotation/json_annotation.dart';
- 2.json_serializable根据当前类,以
part 类名.g.dart
格式生成的文件。
以user.dart为例如下:
part 'user.g.dart';
- 3.在class上标注
@JsonSerializable()
告诉json_serializable哪一个类需要进行转换生成Model类。 - 4.创建必须的构造方法。
- 5.创建必须的对应的工厂构造器。
备注1:
第五步实际就是创建两个方法:
- 提供一个工厂构造方法User.fromJson,该方法实际调用生成文件的UserFromJson方法进行反序列化。
- 提供一个toJson()序列化对象的方法,实际调用生成文件的_$UserToJson()方法,并将调用对象解析生成Map
。
备注2:
-
_$UserFromJson(json)
: 它接收了一个map:Map,并将这个Map里的值映射为我们所需要的实体类对象。我们就可以使用这个方法,将存有json数据的map转化为我们需要的实体类对象。 -
_$UserToJson(this)
: 将调用此方法的对象直接根据字段映射成Map。
而这两个都是私有方法,part让两个文件共享作用域与命名空间,所以我们需要将生成的方法暴露给外部。
备注3:
UserFromJson(json)
和 ToJson()
调用方法,在未执行生成对应的.g.dart文件指令
前该行可能会报错。
part 'computer.g.dart';
和 part 'user.g.dart';
,在未执行生成对应的.g.dart文件指令
前该行可能会报错。
备注4:
toString方法不是必须的,只用来打印输出进行测试。
第3步:生成对应的.g.dart文件指令
该操作有两种指令:一次性生成指令和 持续性生成指令。
一次性生成指令
在项目终端运行下面的指令:
flutter pub run build_runner build
- 该指令是一次性生成JSON序列化的代码。 该指令通过我们的源文件,找出需要生成Model类的源文件(包含@JsonSerializable标注的)来生成对应的.g.dart文件。建议将所有Model类放在一个单独的目录下,然后在该目录下执行命令。
持续性生成指令
如果感觉每次更改Model时都需要执行一次性生成指令比较繁琐,这时可以使用下面的持续生成指令:
flutter pub run build_runner watch
在项目根目录下运行该指令后会启动观察器, 观察器可以监视我们项目中文件的变化,并在需要时自动构建必要的文件。只需启动一次观察器,然后它就会在后台运行,这种方式也很安全。
第4步:测试并打印
final jsonInfo = {
"nickname": "coderTao",
"age": 20,
"courses": ["政治", "高数", "英语"],
"register_date": "2018-2-22",
"computer": {
"brand": "MackBook",
"price": 9999
}
};
final user = User.fromJson(jsonInfo);
print(user);
三. 网页转换
app.quicktype.io 是一个将JSON
转换成模型类的工具网站,目前来看支持大部分常用语言,并且灵活的可选项也非常多:
优点: 这种方式操作起来会比使用json_serializable操作起来更简便一些,并且带下划线字段会自动转换为驼峰命名的属性名。
缺点: 如果数据过于复杂的话,在生成的时候可能会少了某一个类,并且不能进行父类抽取。
四. 编辑器插件
目前Android Studio(或IntelliJ)有几个插件,可以将json文件转成Model类,但插件质量参差不齐,甚至还有一些有抄袭嫌疑,故笔者在此不做优先推荐,读者有兴趣可以自行了解。
Json转Model几种方式总结:
- 手动序列化JSON:比较麻烦,效率低,但新手还是多做尝试和了解比较好。
- json_serializable:效率高,watch很好用。
- 工具网站:效率高,更多功能可选。
总体推荐使用后两种,可以大大提升开发效率,不用埋头去搞一些重复的序列化工作。
由于笔者水平有限,文中如果有错误的地方,或者有更好的方法,还望大神指出。
附上本文的所有 demo 下载链接,【GitHub】。
如果你看完后觉得对你有所帮助,还望在 GitHub 上点个 star。赠人玫瑰,手有余香。