背景
由于Flutter
禁用运行时反射,所以在Flutter
中是没有GSON
,Jackson
这类解析JSON
的库。官方解释运行时反射会干扰Dart
的_tree shaking_
。使用_tree shaking_
我们可以在发版时去除未使用的代码。这可以显着优化应用程序的大小。
由于反射会默认使用所有代码,因此_tree shaking_
会很难工作。这些工具无法知道哪些widget
在运行时未被使用,因此冗余代码很难剥离。使用反射时,应用尺寸无法轻松的进行优化。
手动序列化JSON
使用Flutter
内置的dart:convert
库做基本的JSON
序列化很简单:
Map<String, dynamic> person = JSON.decode(json);
print('${person['name']}');
print('${person['age']');
JSON.decode()
返回一个Map<String, dynamic>
,这意味着我们直到运行时才知道值的类型。这种方法,我们失去了静态类型语言特性,代码非常容易出错。不推荐。
当然我们也可以手动在模型类中序列化JSON
:
class Person {
final String name;
final String age;
User(this.name, this.age);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
age = json['age'];
Map<String, dynamic> toJson() =>
{
'name': name,
'age': age,
};
}
Map personMap = JSON.decode(json);
var person = new Person.fromJson(personMap);
print('${person.name}');
print('${person.age}');
这样我们调用代码可以具有类型安全、自动补全字段以及编译时异常等静态类型语言特性。如果拼写或者类型错误就不会通过编译,而不是在运行时崩溃。
在项目实战中JSON
对象很少会这么简单,各种List
和Map
嵌套的JSON
也是很常见的。如何安全且高效的JSON
转Model
才是我们想要的。
使用 json_serializable
json_serializable是一个自动化的源代码生成器,可以为我们生成JSON
序列化模板。在pubspec.yaml
中添加依赖并执行flutter pub get
:
dependencies:
json_annotation: ^3.0.0
dev_dependencies:
build_runner: ^1.0.0
json_serializable: 3.2.0
这里没有使用最新版本因为我们Flutter
版本为v1.9.1+hotfix.6
,Dart
版本比较低。
- 生成模型类
生成模型类我们使用一位大佬写的json2dart工具。
假设mock
了一个JSON
:
[
{
"name": "小龙女",
"age": "18",
"tele": "13888888888"
},
{
"name": "杨过",
"age": "18",
"tele": "13666666666"
},
{
"name": "尹志平",
"age": "18",
"tele": "13333333333"
}
]
工具使用很简单直接粘贴生成对应的类名称

copy
出来粘在我们创建的模型类中。
import 'package:json_annotation/json_annotation.dart';
part 'person_model.g.dart';
List<PersonModel> getPersonModelList(List<dynamic> list){
List<PersonModel> result = [];
list.forEach((item){
result.add(PersonModel.fromJson(item));
});
return result;
}
@JsonSerializable()
class PersonModel extends Object {
@JsonKey(name: 'name')
String name;
@JsonKey(name: 'age')
String age;
@JsonKey(name: 'tele')
String tele;
PersonModel(this.name,this.age,this.tele,);
factory PersonModel.fromJson(Map<String, dynamic> srcJson) => _$PersonModelFromJson(srcJson);
Map<String, dynamic> toJson() => _$PersonModelToJson(this);
}
这里需要注意这个工具可能有少字段的情况,如果是List
的JSON
只会取其中的第一个(小龙女)来生成模型的字段。也就是如果后面的Item
(杨过,尹志平)字段数量大于第一个也会按照第一个的字段来生成。
- 运行代码生成程序
上面的模型类生成之后会先报错,因为模型类的生成代码还不存在,所以我们需要运行代码生成器来为我们生成序列化模板。
- 一次性生成
flutter packages pub run build_runner build
。 - 持续生成
flutter packages pub run build_runner watch
。
这里选择哪种方式取决于你的改动频率,推荐使用watch
的方式。
- 使用
Map personList = JSON.decode(json);
var list = getPersonModelList(personList);
json_serializable
这种方式,我们可以轻松的生成一个模型类。通过源代码生成器创建一个g.dart
的文件,它具有所有必需的序列化逻辑。
使用工具网站
app.quicktype.io是一个将JSON
转换成模型类的工具网站,目前来看支持大部分常用语言,并且灵活的可选项也非常多:

这里我们还是用上面的JSON
做一下尝试:

生成的模型类是使用了Flutter
内置的dart:convert
做序列化。
// To parse this JSON data, do
//
// final personModel = personModelFromJson(jsonString);
import 'dart:convert';
List<PersonModel> personModelFromJson(String str) => List<PersonModel>.from(json.decode(str).map((x) => PersonModel.fromJson(x)));
String personModelToJson(List<PersonModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class PersonModel {
String name;
String age;
String tele;
PersonModel({
this.name,
this.age,
this.tele,
});
factory PersonModel.fromJson(Map<String, dynamic> json) => PersonModel(
name: json["name"],
age: json["age"],
tele: json["tele"],
);
Map<String, dynamic> toJson() => {
"name": name,
"age": age,
"tele": tele,
};
}
可以看到这个模型类正是我们需要的,使用方式也在上面注释的很清楚,目前来讲这种方式操作起来会比使用json_serializable
操作起来更简便一些。
总结
- 手动序列化JSON:比较麻烦,效率低,但新手还是多做尝试和了解比较好。
json_serializable
:效率高,watch
很好用。- 工具网站:效率高,更多功能可选。
总体推荐使用后两种,可以大大提升开发效率,不用埋头去搞一些重复的序列化工作。