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