很難想象一個(gè)移動(dòng)應(yīng)用程序不需要與Web服務(wù)器通信或在某些時(shí)候容易存儲(chǔ)結(jié)構(gòu)化數(shù)據(jù)预伺。制作網(wǎng)絡(luò)連接的應(yīng)用程序時(shí)将塑,遲早需要消耗一些好的舊JSON禾唁。
本指南介紹了如何在Flutter中使用JSON曙强。它涵蓋了在不同場景中使用哪種JSON解決方案救湖,以及原因。
哪種JSON序列化方法適合我晾咪?
本文介紹了使用JSON的兩種常規(guī)策略:
- 手動(dòng)序列化
- 使用代碼生成進(jìn)行自動(dòng)序列化
不同的項(xiàng)目具有不同的復(fù)雜性和用例收擦。對(duì)于較小的概念驗(yàn)證項(xiàng)目或快速原型,使用代碼生成器可能過度谍倦。對(duì)于具有更多復(fù)雜性的多個(gè)JSON模型的應(yīng)用程序塞赂,手動(dòng)編碼很快就會(huì)變得乏味,重復(fù)昼蛀,并且適用于許多小錯(cuò)誤宴猾。
對(duì)較小的項(xiàng)目使用手動(dòng)序列化
手動(dòng)JSON解碼是指使用內(nèi)置的JSON解碼器 dart:convert
圆存。它涉及將原始JSON字符串傳遞給json.decode()
方法,然后Map<String, dynamic>
在方法返回時(shí)查找所需的值仇哆。它沒有外部依賴性或特定的設(shè)置過程沦辙,它有利于快速驗(yàn)證概念。
當(dāng)項(xiàng)目變大時(shí)讹剔,手動(dòng)解碼效果不佳油讯。手動(dòng)編寫解碼邏輯可能變得難以管理且容易出錯(cuò)。如果在訪問不存在的JSON字段時(shí)出現(xiàn)拼寫錯(cuò)誤延欠,則代碼會(huì)在運(yùn)行時(shí)拋出錯(cuò)誤陌兑。
如果您的項(xiàng)目中沒有很多JSON模型,并且希望快速測試概念由捎,那么手動(dòng)序列化可能就是您想要的方式兔综。有關(guān)手動(dòng)編碼的示例,請(qǐng)參閱 使用dart:convert手動(dòng)序列化JSON狞玛。
使用代碼生成中大型項(xiàng)目
使用代碼生成的JSON序列化意味著使用外部庫為您生成編碼樣板软驰。進(jìn)行一些初始設(shè)置后,您將運(yùn)行一個(gè)文件監(jiān)視器心肪,從您的模型類生成代碼碌宴。例如, json_serializable和 built_value 就是這些類型的庫蒙畴。
這種方法適用于較大的項(xiàng)目。不需要手寫的樣板文件呜象,并且在編譯時(shí)捕獲訪問JSON字段時(shí)的拼寫錯(cuò)誤膳凝。代碼生成的缺點(diǎn)是它需要一些初始設(shè)置。此外恭陡,生成的源文件可能會(huì)在項(xiàng)目導(dǎo)航器中產(chǎn)生視覺混亂蹬音。
當(dāng)您擁有中型或大型項(xiàng)目時(shí),您可能希望使用生成的代碼進(jìn)行JSON序列化休玩。要查看基于JSON編碼的代碼生成示例著淆,請(qǐng)參閱 使用代碼生成庫序列化JSON。
Flutter中是否有GSON / Jackson / Moshi等價(jià)物拴疤?
簡單回答是不永部。
這樣的庫需要使用運(yùn)行時(shí)反射,這在Flutter中被禁用呐矾。運(yùn)行時(shí)反射會(huì)干擾樹抖動(dòng)苔埋,Dart已經(jīng)支持了很長時(shí)間。在樹搖動(dòng)的情況下蜒犯,您可以從發(fā)布版本中“擺脫”未使用的代碼组橄。這顯著優(yōu)化了應(yīng)用程序的大小荞膘。
由于反射使得默認(rèn)情況下隱式使用所有代碼,因此使樹難以振動(dòng)玉工。這些工具無法知道運(yùn)行時(shí)哪些部分未使用羽资,因此冗余代碼很難剝離。使用反射時(shí)遵班,應(yīng)用程序大小無法輕松優(yōu)化屠升。
** dartson怎么樣?**
該dartson庫使用運(yùn)行時(shí)反射费奸,這使得它不兼容flutter弥激。
雖然您不能在Flutter中使用運(yùn)行時(shí)反射,但是某些庫為您提供了類似的易用API愿阐,而是基于代碼生成微服。代碼生成庫部分更詳細(xì)地介紹了此方法。
使用dart:convert手動(dòng)序列化JSON
Flutter中的基本JSON編碼非常簡單缨历。Flutter有一個(gè)內(nèi)置 dart:convert
庫以蕴,包括一個(gè)簡單的JSON編碼器和解碼器。
以下是簡單用戶模型的示例JSON辛孵。
{
"name": "John Smith",
"email": "john@example.com"
}
有了dart:convert
丛肮,您可以通過兩種方式對(duì)此JSON模型進(jìn)行編碼。
序列化JSON內(nèi)聯(lián)
通過查看dart:轉(zhuǎn)換JSON文檔魄缚,您將看到可以通過調(diào)用json.decode
方法解碼JSON 宝与,并使用JSON字符串作為方法參數(shù)。
Map<String, dynamic> user = json.decode(json);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
不幸的是冶匹,json.decode()
只返回a Map<String, dynamic>
习劫,這意味著在運(yùn)行時(shí)之前您不知道值的類型。使用這種方法嚼隘,您將丟失大多數(shù)靜態(tài)類型語言功能:類型安全性诽里,自動(dòng)完成以及最重要的編譯時(shí)異常。您的代碼將立即變得更容易出錯(cuò)飞蛹。
例如谤狡,無論何時(shí)訪問name
或email
字段,都可能會(huì)快速引入拼寫錯(cuò)誤卧檐。由于JSON存在于地圖結(jié)構(gòu)中墓懂,編譯器不知道的拼寫錯(cuò)誤。
在模型類中序列化JSON
通過引入User
在此示例中調(diào)用的普通模型類來對(duì)抗前面提到的問題泄隔。在User
課堂上拒贱,你會(huì)發(fā)現(xiàn):
- 一個(gè)
User.fromJson
構(gòu)造函數(shù),構(gòu)造一個(gè)新的User
從地圖結(jié)構(gòu)實(shí)例。 - 一種
toJson
將User
實(shí)例轉(zhuǎn)換為地圖的方法逻澳。
使用這種方法闸天,調(diào)用代碼可以具有類型安全性,name
和email
字段的自動(dòng)完成以及編譯時(shí)異常斜做。如果您使用拼寫錯(cuò)誤或?qū)⒆侄我暈?code>ints而不是String
s苞氮,則應(yīng)用程序?qū)o法編譯,而不是在運(yùn)行時(shí)崩潰瓤逼。
user.dart
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}
解碼邏輯的責(zé)任現(xiàn)在在模型本身內(nèi)部移動(dòng)笼吟。使用這種新方法,您可以輕松解碼用戶霸旗。
Map userMap = json.decode(json);
var user = new User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
要對(duì)用戶進(jìn)行編碼贷帮,請(qǐng)將User
對(duì)象傳遞給json.encode
方法。您不需要調(diào)用該toJson
方法诱告,因?yàn)?code>json.encode 已經(jīng)為您完成了撵枢。
String json = json.encode(user);
使用這種方法,調(diào)用代碼根本不必?fù)?dān)心JSON序列化精居。但是锄禽,模型類仍然必須。在生產(chǎn)應(yīng)用程序中靴姿,您需要確保序列化正常工作沃但。在實(shí)踐中,這些User.fromJson
和User.toJson
方法都需要進(jìn)行單元測試以驗(yàn)證正確的行為佛吓。
但是宵晚,現(xiàn)實(shí)場景通常不那么簡單。您不太可能使用如此小的JSON響應(yīng)维雇。嵌套的JSON對(duì)象也是常用的坝疼。
如果有一些東西可以為您處理JSON編碼和解碼,那就太好了谆沃。幸運(yùn)的是,有仪芒!
使用代碼生成庫序列化JSON
雖然還有其他庫可用唁影,但本指南使用 json_serializable包,這是一個(gè)自動(dòng)生成的源代碼生成器掂名,可為您生成JSON序列化樣板据沈。
由于序列化代碼不再是手動(dòng)或手動(dòng)維護(hù)的,因此可以最大限度地降低在運(yùn)行時(shí)出現(xiàn)JSON序列化異常的風(fēng)險(xiǎn)饺蔑。
在項(xiàng)目中設(shè)置json_serializable
要包含json_serializable
在項(xiàng)目中锌介,您需要一個(gè)常規(guī)依賴項(xiàng)和兩個(gè)dev依賴項(xiàng)。簡而言之,dev依賴項(xiàng) 是我們的應(yīng)用程序源代碼中未包含的依賴項(xiàng) - 它們僅在開發(fā)環(huán)境中使用孔祸。
可以通過遵循 JSON可序列化示例中的pubspec文件來查看這些必需依賴項(xiàng)的最新版本 隆敢。
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
flutter packages get
在項(xiàng)目根文件夾中運(yùn)行(或單擊 編輯器中的Packages Get)以在項(xiàng)目中使用這些新的依賴項(xiàng)。
以json_serializable方式創(chuàng)建模型類
以下顯示如何將User類轉(zhuǎn)換為一個(gè)類json_serializable 崔慧。為簡單起見拂蝎,此代碼使用先前示例中的簡化JSON模型。
user.dart
class User {
final String name;
final String email;
User(this.name, this.email);
User.fromJson(Map<String, dynamic> json)
: name = json['name'],
email = json['email'];
Map<String, dynamic> toJson() =>
{
'name': name,
'email': email,
};
}
采用這種設(shè)置惶室,源代碼生成器用于編碼和將編碼生成代碼name
和email
從JSON字段温自。
如果需要,還可以輕松自定義命名策略皇钞。例如悼泌,如果API返回帶有snake_case的對(duì)象,并且您想在模型中使用 lowerCamelCase夹界,則可以使用@JsonKey
帶有name參數(shù)的注釋:
/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: 'registration_date_millis')
final int registrationDateMillis;
運(yùn)行代碼生成實(shí)用程序
json_serializable
第一次創(chuàng)建類時(shí)馆里,您將收到類似于下圖所示的錯(cuò)誤。
這些錯(cuò)誤完全正常,僅僅是因?yàn)槟P皖惖纳纱a尚不存在趾痘。要解決此問題慢哈,請(qǐng)運(yùn)行生成序列化樣板的代碼生成器。
有兩種運(yùn)行代碼生成器的方法永票。
一次性代碼生成
通過flutter packages pub run build_runner build
在項(xiàng)目根目錄中運(yùn)行卵贱,可以在需要時(shí)為模型生成JSON序列化代碼。這會(huì)觸發(fā)一次性構(gòu)建侣集,該構(gòu)建遍歷源文件键俱,選擇相關(guān)文件,并為它們生成必要的序列化代碼世分。
雖然這很方便编振,但如果您不必每次在模型類中進(jìn)行更改時(shí)都必須手動(dòng)運(yùn)行構(gòu)建,那將是很好的臭埋。
不斷生成代碼
一個(gè)觀察者踪央,使我們的源代碼生成的過程更加方便。它會(huì)監(jiān)視項(xiàng)目文件中的更改瓢阴,并在需要時(shí)自動(dòng)構(gòu)建必要的文件畅蹂。通過flutter packages pub run build_runner watch
在項(xiàng)目根目錄中運(yùn)行來啟動(dòng)觀察程序 。
啟動(dòng)觀察者一次并讓它在后臺(tái)運(yùn)行是安全的荣恐。
使用json_serializable模型
要以這種json_serializable
方式解碼JSON字符串液斜,您實(shí)際上沒有對(duì)我們以前的代碼進(jìn)行任何更改累贤。
Map userMap = json.decode(json);
var user = User.fromJson(userMap);
編碼也是如此。調(diào)用API與以前相同少漆。
String json = json.encode(user);
有了json_serializable
臼膏,您可以忘記User
該類中的任何手動(dòng)JSON序列化 。源代碼生成器創(chuàng)建一個(gè)名為的文件user.g.dart
检疫,該文件具有所有必需的序列化邏輯讶请。您不再需要編寫自動(dòng)化測試來確保序列化工作 - 現(xiàn)在圖書館有責(zé)任確保序列化正常工作。
進(jìn)一步參考
有關(guān)更多信息屎媳,請(qǐng)參閱以下資源: