種JSON序列化方法适合我?
本文介紹了(le)使用JSON的兩個常規策略:
手動序列化和(hé)反序列化
通(tōng)過代碼生成自(zì)動序列化和(hé)反序列化
不同的項目具有不同的複雜(zá)度和(hé)場景。對于較小項目,使用代碼生成器可(kě)能(néng)會過度。對于具有多個JSON model的複雜(zá)應用程序,手動序列化可(kě)能(néng)會比較重複,并會很(hěn)容易出錯。
小項目手動序列化
手動JSON序列化是指使使用dart:convert中內(nèi)置的JSON解碼器。它将原始JSON字符串傳遞給JSON.decode() 方法,然後在返回的Map
當您的項目變大時(shí),手動編寫序列化邏輯可(kě)能(néng)變得難以管理且容易出錯。如(rú)果您在訪問(wèn)未提供的JSON字段時(shí)輸入了(le)一(yī)個錯誤的字段,則您的代碼将會在運行(xíng)時(shí)會引發錯誤。
如(rú)果您的項目中JSON model并不多,并且希望快(kuài)速測試一(yī)下(xià),那(nà)麽手動序列化可(kě)能(néng)會很(hěn)方便。
在大中型項目中使用代碼生成
代碼生成功能(néng)的JSON序列化是指通(tōng)過外(wài)部庫為(wèi)您自(zì)動生成序列化模闆。它需要(yào)一(yī)些初始設置,并運行(xíng)一(yī)個文件觀察器,從(cóng)您的model類生成代碼。 例如(rú),json_serializable和(hé)built_value就是這(zhè)樣的庫。
這(zhè)種方法适用于較大的項目。不需要(yào)手寫,如(rú)果訪問(wèn)JSON字段時(shí)拼寫錯誤,這(zhè)會在編譯時(shí)捕獲的。代碼生成的不利之處在于它涉及到一(yī)些初始設置。另外(wài),生成的源文件可(kě)能(néng)會在項目導航器會顯得混亂。
當您有一(yī)個中型或大型項目時(shí),您可(kě)能(néng)想要(yào)使用代碼生成JSON序列化。
Flutter中是否有GSON / Jackson / Moshi?
簡單的回答(dá)是沒有.
這(zhè)樣的庫需要(yào)使用運行(xíng)時(shí)反射,這(zhè)在Flutter中是禁用的。運行(xíng)時(shí)反射會幹擾Dart的_tree shaking_。使用_tree shaking_,我們可(kě)以在發版時(shí)“去除”未使用的代碼。這(zhè)可(kě)以顯着優化應用程序的大小。
由于反射會默認使用所有代碼,因此_tree shaking_會很(hěn)難工(gōng)作(zuò)。這(zhè)些工(gōng)具無法知道(dào)哪些widget在運行(xíng)時(shí)未被使用,因此冗餘代碼很(hěn)難剝離。使用反射時(shí),應用尺寸無法輕松的進行(xíng)優化。
dartson呢?
dartson 使用了(le)運行(xíng)時(shí)反射 runtime,所以不能(néng)在Flutter中使用它.
雖然我們不能(néng)在Flutter中使用運行(xíng)時(shí)反射,但(dàn)有些庫為(wèi)我們提供了(le)類似易于使用的API,但(dàn)它們是基于代碼生成的。這(zhè)種方法在代碼生成庫部分有更詳細的介紹。
使用 dart:convert手動序列化JSON
Flutter中基本的JSON序列化非常簡單。Flutter有一(yī)個內(nèi)置dart:convert庫,其中包含一(yī)個簡單的JSON編碼器和(hé)解碼器。
以下(xià)是一(yī)個簡單的user model的示例JSON。
{
"name": "John Smith",
"email": "john@example.com"
}
有了(le)dart:convert,我們可(kě)以用兩種方式來序列化這(zhè)個JSON model。我們來看(kàn)看(kàn)這(zhè)兩種方法:
內(nèi)連序列化JSON
通(tōng)過查看(kàn)dart:轉換JSON文檔,我們發現可(kě)以通(tōng)過調用JSON.decode方法來解碼JSON ,使用JSON字符串作(zuò)為(wèi)參數(shù)。
Map
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
不幸的是,JSON.decode()僅返回一(yī)個Map
例如(rú),當我們訪問(wèn)name或email字段時(shí),我們輸入的很(hěn)快(kuài),導緻字段名打錯了(le)。但(dàn)由于這(zhè)個JSON在map結構中,所以編譯器不知道(dào)這(zhè)個錯誤的字段名(譯者語:所以編譯時(shí)不會報錯)。
在模型類中序列化JSON
我們可(kě)以通(tōng)過引入一(yī)個簡單的模型類(model class)來解決前面提到的問(wèn)題,我們稱之為(wèi)User。在User類內(nèi)部,我們有:
一(yī)個User.fromJson 構造函數(shù), 用于從(cóng)一(yī)個map構造出一(yī)個 User實例 map structure
一(yī)個toJson 方法, 将 User 實例轉化為(wèi)一(yī)個map.
這(zhè)樣,調用代碼現在可(kě)以具有類型安全、自(zì)動補全字段(name和(hé)email)以及編譯時(shí)異常。如(rú)果我們将拼寫錯誤或字段視(shì)為(wèi)int類型而不是String, 那(nà)麽我們的應用程序就不會通(tōng)過編譯,而不是在運行(xíng)時(shí)崩潰。
user.dart
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map
: name = json['name'],
email = json['email'];
Map
{
'name': name,
'email': email,
};
}
現在,序列化邏輯移到了(le)模型本身內(nèi)部。采用這(zhè)種新方法,我們可(kě)以非常容易地(dì)反序列化user。
Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
要(yào)序列化一(yī)個user,我們隻是将該User對象傳遞給該JSON.encode方法。我們不需要(yào)手動調用toJson這(zhè)個方法,因為(wèi)JSON.encode已經為(wèi)我們做(zuò)了(le)。
String json = JSON.encode(user);
這(zhè)樣,調用代碼就不用擔心JSON序列化了(le)。但(dàn)是,model類還是必須的。在生産應用程序中,我們希望确保序列化正常工(gōng)作(zuò)。在實踐中,User.fromJson和(hé)User.toJson方法都(dōu)需要(yào)單元測試到位,以驗證正确的行(xíng)為(wèi)。
另外(wài),實際場景中,JSON對象很(hěn)少(shǎo)會這(zhè)麽簡單,嵌套的JSON對象并不罕見(jiàn)。
如(rú)果有什麽能(néng)為(wèi)我們自(zì)動處理JSON序列化,那(nà)将會非常好。幸運的是,有!
使用代碼生成庫序列化JSON
盡管還有其他(tā)庫可(kě)用,但(dàn)在本教程中,我們使用了(le)json_serializable package包。 它是一(yī)個自(zì)動化的源代碼生成器,可(kě)以為(wèi)我們生成JSON序列化模闆。
由于序列化代碼不再由我們手寫和(hé)維護,我們将運行(xíng)時(shí)産生JSON序列化異常的風(fēng)險降至最低(dī)。
在項目中設置json_serializable
要(yào)包含json_serializable到我們的項目中,我們需要(yào)一(yī)個常規和(hé)兩個開(kāi)發依賴項。簡而言之,開(kāi)發依賴項是不包含在我們的應用程序源代碼中的依賴項。
通(tōng)過此鏈接可(kě)以查看(kàn)這(zhè)些所需依賴項的最新版本 。
pubspec.yaml
dependencies:
# Your other regular dependencies here
json_annotation: ^2.0.0
dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.0.0
json_serializable: ^2.0.0
在您的項目根文件夾中運行(xíng) flutter packages get (或者在編輯器中點擊 “Packages Get”) 以在項目中使用這(zhè)些新的依賴項.
以json_serializable的方式創建model類
讓我們看(kàn)看(kàn)如(rú)何将我們的User類轉換為(wèi)一(yī)個json_serializable。為(wèi)了(le)簡單起見(jiàn),我們使用前面示例中的簡化JSON model。
user.dart
import 'package:json_annotation/json_annotation.dart';
// user.g.dart 将在我們運行(xíng)生成命令後自(zì)動生成
part 'user.g.dart';
///這(zhè)個标注是告訴生成器,這(zhè)個類是需要(yào)生成Model類的
@JsonSerializable()
class User{
User(this.name, this.email);
String name;
String email;
//不同的類使用不同的mixin即可(kě)
factory User.fromJson(Map
Map
}
有了(le)這(zhè)個設置,源碼生成器将生成用于序列化name和(hé)email字段的JSON代碼。
如(rú)果需要(yào),自(zì)定義命名策略也很(hěn)容易。例如(rú),如(rú)果我們正在使用的API返回帶有_snake_case_的對象,但(dàn)我們想在我們的模型中使用_lowerCamelCase_, 那(nà)麽我們可(kě)以使用@JsonKey标注:
/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: 'registration_date_millis')
final int registrationDateMillis;
運行(xíng)代碼生成程序
json_serializable第一(yī)次創建類時(shí),您會看(kàn)到與下(xià)圖類似的錯誤。
IDE warning when the generated code for a model class does not exist
yet.
這(zhè)些錯誤是完全正常的,這(zhè)是因為(wèi)model類的生成代碼還不存在。為(wèi)了(le)解決這(zhè)個問(wèn)題,我們必須運行(xíng)代碼生成器來為(wèi)我們生成序列化模闆。
There are two ways of running the code generator. 有兩種運行(xíng)代碼生成器的方法:
一(yī)次性生成
通(tōng)過在我們的項目根目錄下(xià)運行(xíng)flutter packages pub run build_runner build,我們可(kě)以在需要(yào)時(shí)為(wèi)我們的model生成json序列化代碼。 這(zhè)觸發了(le)一(yī)次性構建,它通(tōng)過我們的源文件,挑選相關的并為(wèi)它們生成必要(yào)的序列化代碼。
雖然這(zhè)非常方便,但(dàn)如(rú)果我們不需要(yào)每次在model類中進行(xíng)更改時(shí)都(dōu)要(yào)手動運行(xíng)構建命令的話會更好。
持續生成
使用_watcher_可(kě)以使我們的源代碼生成的過程更加方便。它會監視(shì)我們項目中文件的變化,并在需要(yào)時(shí)自(zì)動構建必要(yào)的文件。我們可(kě)以通(tōng)過flutter packages pub run build_runner watch在項目根目錄下(xià)運行(xíng)來啓動_watcher_。
隻需啓動一(yī)次觀察器,然後并讓它在後台運行(xíng),這(zhè)是安全的。
使用json_serializable模型
要(yào)通(tōng)過json_serializable方式反序列化JSON字符串,我們不需要(yào)對先前的代碼進行(xíng)任何更改。
Map userMap = JSON.decode(json);
var user = new User.fromJson(userMap);
序列化也一(yī)樣。調用API與之前相同。
String json = JSON.encode(user);
有了(le)json_serializable,我們可(kě)以在User類上(shàng)忘記任何手動的JSON序列化 。源代碼生成器創建一(yī)個名為(wèi)user.g.dart的文件,它具有所有必需的序列化邏輯。 現在,我們不必編寫自(zì)動化測試來确保序列化的正常工(gōng)作(zuò) - 這(zhè)個庫會确保序列化工(gōng)作(zuò)正常。
網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發