[譯]Flutter JSON和序列化
[譯]Flutter JSON和序列化
很難想象一個移動應用程序不需要與Web服務器通信或在某些時候容易存儲結構化數據。制作網絡連接的應用程序時,遲早需要消耗一些好的舊JSON。
本指南介紹了如何在Flutter中使用JSON。它涵蓋了在不同場景中使用哪種JSON解決方案,以及原因。
哪種JSON序列化方法適合我?
本文介紹了使用JSON的兩種常規策略:
- 手動序列化
- 使用代碼生成進行自動序列化
不同的項目具有不同的復雜性和用例。對於較小的概念驗證項目或快速原型,使用代碼生成器可能過度。對於具有更多復雜性的多個JSON模型的應用程序,手動編碼很快就會變得乏味,重復,並且適用於許多小錯誤。
對較小的項目使用手動序列化
手動JSON解碼是指使用內置的JSON解碼器 dart:convert
。它涉及將原始JSON字符串傳遞給json.decode()
方法,然後Map<String, dynamic>
在方法返回時查找所需的值。它沒有外部依賴性或特定的設置過程,它有利於快速驗證概念。
當項目變大時,手動解碼效果不佳。手動編寫解碼邏輯可能變得難以管理且容易出錯。如果在訪問不存在的JSON字段時出現拼寫錯誤,則代碼會在運行時拋出錯誤。
如果您的項目中沒有很多JSON模型,並且希望快速測試概念,那麽手動序列化可能就是您想要的方式。有關手動編碼的示例,請參閱 使用dart:convert手動序列化JSON。
使用代碼生成中大型項目
使用代碼生成的JSON序列化意味著使用外部庫為您生成編碼樣板。進行一些初始設置後,您將運行一個文件監視器,從您的模型類生成代碼。例如, json_serializable和 built_value 就是這些類型的庫。
這種方法適用於較大的項目。不需要手寫的樣板文件,並且在編譯時捕獲訪問JSON字段時的拼寫錯誤。代碼生成的缺點是它需要一些初始設置。此外,生成的源文件可能會在項目導航器中產生視覺混亂。
當您擁有中型或大型項目時,您可能希望使用生成的代碼進行JSON序列化。要查看基於JSON編碼的代碼生成示例,請參閱 使用代碼生成庫序列化JSON。
Flutter中是否有GSON / Jackson / Moshi等價物?
簡單回答是不。
這樣的庫需要使用運行時反射,這在Flutter中被禁用。運行時反射會幹擾樹抖動,Dart已經支持了很長時間。在樹搖動的情況下,您可以從發布版本中“擺脫”未使用的代碼。這顯著優化了應用程序的大小。
由於反射使得默認情況下隱式使用所有代碼,因此使樹難以振動。這些工具無法知道運行時哪些部分未使用,因此冗余代碼很難剝離。使用反射時,應用程序大小無法輕松優化。
** dartson怎麽樣?**
該dartson庫使用運行時反射,這使得它不兼容flutter。
雖然您不能在Flutter中使用運行時反射,但是某些庫為您提供了類似的易用API,而是基於代碼生成。代碼生成庫部分更詳細地介紹了此方法。
使用dart:convert手動序列化JSON
Flutter中的基本JSON編碼非常簡單。Flutter有一個內置 dart:convert
庫,包括一個簡單的JSON編碼器和解碼器。
以下是簡單用戶模型的示例JSON。
{
"name": "John Smith",
"email": "[email protected]"
}
有了dart:convert
,您可以通過兩種方式對此JSON模型進行編碼。
序列化JSON內聯
通過查看dart:轉換JSON文檔,您將看到可以通過調用json.decode
方法解碼JSON ,並使用JSON字符串作為方法參數。
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>
,這意味著在運行時之前您不知道值的類型。使用這種方法,您將丟失大多數靜態類型語言功能:類型安全性,自動完成以及最重要的編譯時異常。您的代碼將立即變得更容易出錯。
例如,無論何時訪問name
或email
字段,都可能會快速引入拼寫錯誤。由於JSON存在於地圖結構中,編譯器不知道的拼寫錯誤。
在模型類中序列化JSON
通過引入User
在此示例中調用的普通模型類來對抗前面提到的問題。在User
課堂上,你會發現:
- 一個
User.fromJson
構造函數,構造一個新的User
從地圖結構實例。 - 一種
toJson
將User
實例轉換為地圖的方法。
使用這種方法,調用代碼可以具有類型安全性,name
和email
字段的自動完成以及編譯時異常。如果您使用拼寫錯誤或將字段視為int
s而不是String
s,則應用程序將無法編譯,而不是在運行時崩潰。
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,
};
}
解碼邏輯的責任現在在模型本身內部移動。使用這種新方法,您可以輕松解碼用戶。
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
對象傳遞給json.encode
方法。您不需要調用該toJson
方法,因為json.encode
已經為您完成了。
String json = json.encode(user);
使用這種方法,調用代碼根本不必擔心JSON序列化。但是,模型類仍然必須。在生產應用程序中,您需要確保序列化正常工作。在實踐中,這些User.fromJson
和User.toJson
方法都需要進行單元測試以驗證正確的行為。
但是,現實場景通常不那麽簡單。您不太可能使用如此小的JSON響應。嵌套的JSON對象也是常用的。
如果有一些東西可以為您處理JSON編碼和解碼,那就太好了。幸運的是,有!
使用代碼生成庫序列化JSON
雖然還有其他庫可用,但本指南使用 json_serializable包,這是一個自動生成的源代碼生成器,可為您生成JSON序列化樣板。
由於序列化代碼不再是手動或手動維護的,因此可以最大限度地降低在運行時出現JSON序列化異常的風險。
在項目中設置json_serializable
要包含json_serializable
在項目中,您需要一個常規依賴項和兩個dev依賴項。簡而言之,dev依賴項 是我們的應用程序源代碼中未包含的依賴項 - 它們僅在開發環境中使用。
可以通過遵循 JSON可序列化示例中的pubspec文件來查看這些必需依賴項的最新版本 。
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
在項目根文件夾中運行(或單擊 編輯器中的Packages Get)以在項目中使用這些新的依賴項。
以json_serializable方式創建模型類
以下顯示如何將User類轉換為一個類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,
};
}
采用這種設置,源代碼生成器用於編碼和將編碼生成代碼name
和email
從JSON字段。
如果需要,還可以輕松自定義命名策略。例如,如果API返回帶有snake_case的對象,並且您想在模型中使用 lowerCamelCase,則可以使用@JsonKey
帶有name參數的註釋:
/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: ‘registration_date_millis‘)
final int registrationDateMillis;
運行代碼生成實用程序
json_serializable
第一次創建類時,您將收到類似於下圖所示的錯誤。
當模型類的生成代碼尚不存在時,IDE警告。
這些錯誤完全正常,僅僅是因為模型類的生成代碼尚不存在。要解決此問題,請運行生成序列化樣板的代碼生成器。
有兩種運行代碼生成器的方法。
一次性代碼生成
通過flutter packages pub run build_runner build
在項目根目錄中運行,可以在需要時為模型生成JSON序列化代碼。這會觸發一次性構建,該構建遍歷源文件,選擇相關文件,並為它們生成必要的序列化代碼。
雖然這很方便,但如果您不必每次在模型類中進行更改時都必須手動運行構建,那將是很好的。
不斷生成代碼
一個觀察者,使我們的源代碼生成的過程更加方便。它會監視項目文件中的更改,並在需要時自動構建必要的文件。通過flutter packages pub run build_runner watch
在項目根目錄中運行來啟動觀察程序 。
啟動觀察者一次並讓它在後臺運行是安全的。
使用json_serializable模型
要以這種json_serializable
方式解碼JSON字符串,您實際上沒有對我們以前的代碼進行任何更改。
Map userMap = json.decode(json);
var user = User.fromJson(userMap);
編碼也是如此。調用API與以前相同。
String json = json.encode(user);
有了json_serializable
,您可以忘記User
該類中的任何手動JSON序列化 。源代碼生成器創建一個名為的文件user.g.dart
,該文件具有所有必需的序列化邏輯。您不再需要編寫自動化測試來確保序列化工作 - 現在圖書館有責任確保序列化正常工作。
進一步參考
有關更多信息,請參閱以下資源:
- JsonCodec文檔
- Pub中的json_serializable包
- GitHub中的json_serializable示例
[譯]Flutter JSON和序列化