背景: 在開(kāi)發(fā)中,服務(wù)端通常返回Json數(shù)據(jù)容为,我們需要將Json數(shù)據(jù)轉(zhuǎn)模型對(duì)象來(lái)使用壁却。一般情況下,我們會(huì)使用一些第三方庫(kù)來(lái)動(dòng)態(tài)轉(zhuǎn)化Model阳懂,但是Flutter中沒(méi)有像Java的Gson/Jackson這類(lèi)Json序列化類(lèi)庫(kù)梅尤,因?yàn)镕lutter中禁用運(yùn)行時(shí)反射柜思。官方解釋是運(yùn)行時(shí)反射會(huì)干擾Dart的
tree shaking
,使用tree shaking
可以在release版中去除未使用的代碼巷燥,這可以顯著優(yōu)化應(yīng)用程序的大小赡盘。由于反射會(huì)默認(rèn)應(yīng)用到所有代碼,因此tree shaking
會(huì)很難工作缰揪,因?yàn)樵趩⒂梅瓷鋾r(shí)很難知道哪些代碼未被使用陨享,因此冗余代碼很難剝離,所以Flutter中禁用了Dart的反射功能钝腺,而正因如此也就無(wú)法實(shí)現(xiàn)動(dòng)態(tài)轉(zhuǎn)化Model的功能抛姑。
在此基礎(chǔ)上,接下來(lái)我們看下Flutter中還有哪幾種Json轉(zhuǎn)模型的方式:
一. 手動(dòng)轉(zhuǎn)化
在上篇[Flutter] 07-Flutter中反序列化Json已經(jīng)通過(guò)6個(gè)示例分析過(guò)了, 這里不再討論艳狐。
二. json_serializable
json_serializable是dart官方推薦和提供的JSON轉(zhuǎn)Model的方式:
- 一個(gè)自動(dòng)化源代碼生成器來(lái)為你生成 JSON 序列化數(shù)據(jù)模板定硝;
- 由于序列化數(shù)據(jù)代碼不再需要手動(dòng)編寫(xiě)或者維護(hù),你可以將序列化 JSON 數(shù)據(jù)在運(yùn)行時(shí)的異常風(fēng)險(xiǎn)降到最低僵驰;
第1步:添加相關(guān)的依賴(lài)
依賴(lài)分為項(xiàng)目依賴(lài)(dependencies)喷斋,開(kāi)發(fā)依賴(lài)(dev_dependencies),在pubspec.yaml
中添加如下依賴(lài):
dependencies:
json_annotation:^3.0.1
dev_dependencies:
json_serializable:^3.2.5
build_runner:^1.8.0
- 注意:添加后需要執(zhí)行
flutter pub get
確保我們的項(xiàng)目中有這些依賴(lài)蒜茴。 - 注意:yaml配置文件對(duì)于縮進(jìn)要求十分嚴(yán)格星爪,下面的
build_runner
和json_serializable
應(yīng)該是與flutter_test
平級(jí)的,千萬(wàn)不要寫(xiě)在flutter_test縮進(jìn)后粉私,這樣它會(huì)認(rèn)為這兩個(gè)是flutter_test的子集目錄顽腾!
由于很多朋友在這一步遇到了問(wèn)題,這里貼出源碼:
第2步:以json_serializable 的方式創(chuàng)建模型類(lèi)
- 根據(jù)下面簡(jiǎn)單Json數(shù)據(jù)創(chuàng)建模型類(lèi):
final jsonInfo = {
"nickname": "coderTao",
"age": 20,
"courses": ["政治", "高數(shù)", "英語(yǔ)"],
"register_date": "2018-2-22",
"computer": {
"brand": "MackBook",
"price": 9999
}
};
- User類(lèi)的代碼:
// 1.import 導(dǎo)入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
import 'computer_model.dart';
// 2.user.g.dart 將在我們運(yùn)行生成命令后json_serializable幫我們自動(dòng)生成.g.dart文件诺核,在未執(zhí)行命令前該行可能會(huì)報(bào)錯(cuò)
part 'user_model.g.dart';
// 3.這個(gè)標(biāo)注是告訴生成器抄肖,這個(gè)類(lèi)是需要生成Model類(lèi)的
@JsonSerializable()
class User {
String name;
int age;
//顯式關(guān)聯(lián)JSON字段名與Model屬性的對(duì)應(yīng)關(guān)系,
// 如下將屬性registerDate和register_date字段關(guān)聯(lián)
@JsonKey(name: "register_date")
String registerDate;
List<String> courses;
Computer computer;
// 4.必須的構(gòu)造方法
User(this.name, this.age, this.registerDate, this.courses, this.computer);
// 5.必須有的對(duì)應(yīng)工廠構(gòu)造器
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
//這里 toString方法不是必須的, 只是用測(cè)試數(shù)據(jù)
@override
String toString() {
return'User{name: $name, age: ${age}, registerDate: $registerDate, courses: $courses, computer: $computer}';
}
}
- Computer類(lèi)的代碼:
// 1.import 導(dǎo)入json_annotation.dart
import 'package:json_annotation/json_annotation.dart';
// 2.computer.g.dart 將在我們運(yùn)行生成命令后json_serializable幫我們自動(dòng)生成.g.dart文件,在未執(zhí)行命令前該行可能會(huì)報(bào)錯(cuò)
part 'computer.g.dart';
// 3.這個(gè)標(biāo)注告訴json_serializable哪一個(gè)類(lèi)需要進(jìn)行轉(zhuǎn)換生成Model類(lèi)
@JsonSerializable()
class Computer {
String brand;
double price;
//4.必須的構(gòu)造方法
Computer(this.brand, this.price);
//5.必須有的對(duì)應(yīng)工廠構(gòu)造器
factory Computer.fromJson(Map<String, dynamic> json) => _$ComputerFromJson(json);
Map<String, dynamic> toJson() => _$ComputerToJson(this);
//這里 toString方法不是必須的, 只是用測(cè)試數(shù)據(jù)
@override
String toString() {
return'Computer{brand: $brand, price: $price}';
}
}
最后總結(jié)一下以json_serializable 的方式創(chuàng)建模型類(lèi)必須5步:
- 1.import 導(dǎo)入
json_annotation.dart
窖杀。
import 'package:json_annotation/json_annotation.dart';
- 2.json_serializable根據(jù)當(dāng)前類(lèi)漓摩,以
part 類(lèi)名.g.dart
格式生成的文件。
以u(píng)ser.dart為例如下:
part 'user.g.dart';
- 3.在class上標(biāo)注
@JsonSerializable()
告訴json_serializable哪一個(gè)類(lèi)需要進(jìn)行轉(zhuǎn)換生成Model類(lèi)入客。 - 4.創(chuàng)建必須的構(gòu)造方法管毙。
- 5.創(chuàng)建必須的對(duì)應(yīng)的工廠構(gòu)造器。
備注1:
第五步實(shí)際就是創(chuàng)建兩個(gè)方法:
- 提供一個(gè)工廠構(gòu)造方法User.fromJson桌硫,該方法實(shí)際調(diào)用生成文件的UserFromJson方法進(jìn)行反序列化夭咬。
- 提供一個(gè)toJson()序列化對(duì)象的方法,實(shí)際調(diào)用生成文件的_$UserToJson()方法铆隘,并將調(diào)用對(duì)象解析生成Map<String ,dynamic>卓舵。
備注2:
-
_$UserFromJson(json)
: 它接收了一個(gè)map:Map<String, dynamic>,并將這個(gè)Map里的值映射為我們所需要的實(shí)體類(lèi)對(duì)象膀钠。我們就可以使用這個(gè)方法掏湾,將存有json數(shù)據(jù)的map轉(zhuǎn)化為我們需要的實(shí)體類(lèi)對(duì)象裹虫。 -
_$UserToJson(this)
: 將調(diào)用此方法的對(duì)象直接根據(jù)字段映射成Map。
而這兩個(gè)都是私有方法忘巧,part讓兩個(gè)文件共享作用域與命名空間恒界,所以我們需要將生成的方法暴露給外部。
備注3:
UserFromJson(json)
和 ToJson()
調(diào)用方法砚嘴,在未執(zhí)行生成對(duì)應(yīng)的.g.dart文件指令
前該行可能會(huì)報(bào)錯(cuò)十酣。
part 'computer.g.dart';
和 part 'user.g.dart';
,在未執(zhí)行生成對(duì)應(yīng)的.g.dart文件指令
前該行可能會(huì)報(bào)錯(cuò)际长。
備注4:
toString方法不是必須的耸采,只用來(lái)打印輸出進(jìn)行測(cè)試。
第3步:生成對(duì)應(yīng)的.g.dart文件指令
該操作有兩種指令:一次性生成指令和 持續(xù)性生成指令工育。
一次性生成指令
在項(xiàng)目終端運(yùn)行下面的指令:
flutter pub run build_runner build
- 該指令是一次性生成JSON序列化的代碼虾宇。 該指令通過(guò)我們的源文件,找出需要生成Model類(lèi)的源文件(包含@JsonSerializable標(biāo)注的)來(lái)生成對(duì)應(yīng)的.g.dart文件如绸。建議將所有Model類(lèi)放在一個(gè)單獨(dú)的目錄下嘱朽,然后在該目錄下執(zhí)行命令。
持續(xù)性生成指令
如果感覺(jué)每次更改Model時(shí)都需要執(zhí)行一次性生成指令比較繁瑣怔接,這時(shí)可以使用下面的持續(xù)生成指令:
flutter pub run build_runner watch
在項(xiàng)目根目錄下運(yùn)行該指令后會(huì)啟動(dòng)觀察器, 觀察器可以監(jiān)視我們項(xiàng)目中文件的變化搪泳,并在需要時(shí)自動(dòng)構(gòu)建必要的文件。只需啟動(dòng)一次觀察器扼脐,然后它就會(huì)在后臺(tái)運(yùn)行岸军,這種方式也很安全。
第4步:測(cè)試并打印
final jsonInfo = {
"nickname": "coderTao",
"age": 20,
"courses": ["政治", "高數(shù)", "英語(yǔ)"],
"register_date": "2018-2-22",
"computer": {
"brand": "MackBook",
"price": 9999
}
};
final user = User.fromJson(jsonInfo);
print(user);
三. 網(wǎng)頁(yè)轉(zhuǎn)換
app.quicktype.io 是一個(gè)將JSON
轉(zhuǎn)換成模型類(lèi)的工具網(wǎng)站瓦侮,目前來(lái)看支持大部分常用語(yǔ)言艰赞,并且靈活的可選項(xiàng)也非常多:
優(yōu)點(diǎn): 這種方式操作起來(lái)會(huì)比使用json_serializable操作起來(lái)更簡(jiǎn)便一些,并且?guī)聞澗€字段會(huì)自動(dòng)轉(zhuǎn)換為駝峰命名的屬性名肚吏。
缺點(diǎn): 如果數(shù)據(jù)過(guò)于復(fù)雜的話方妖,在生成的時(shí)候可能會(huì)少了某一個(gè)類(lèi),并且不能進(jìn)行父類(lèi)抽取罚攀。
四. 編輯器插件
目前Android Studio(或IntelliJ)有幾個(gè)插件吁断,可以將json文件轉(zhuǎn)成Model類(lèi),但插件質(zhì)量參差不齊坞生,甚至還有一些有抄襲嫌疑,故筆者在此不做優(yōu)先推薦掷伙,讀者有興趣可以自行了解是己。
Json轉(zhuǎn)Model幾種方式總結(jié):
- 手動(dòng)序列化JSON:比較麻煩,效率低任柜,但新手還是多做嘗試和了解比較好卒废。
- json_serializable:效率高沛厨,watch很好用。
- 工具網(wǎng)站:效率高摔认,更多功能可選逆皮。
總體推薦使用后兩種,可以大大提升開(kāi)發(fā)效率参袱,不用埋頭去搞一些重復(fù)的序列化工作电谣。
由于筆者水平有限,文中如果有錯(cuò)誤的地方抹蚀,或者有更好的方法剿牺,還望大神指出。
附上本文的所有 demo 下載鏈接环壤,【GitHub】晒来。
如果你看完后覺(jué)得對(duì)你有所幫助,還望在 GitHub 上點(diǎn)個(gè) star郑现。贈(zèng)人玫瑰湃崩,手有余香。