flutter中的JSON和序列化方法及使用詳解
引言
很難想象一款移動應(yīng)用程序不需要與web服務(wù)器通信,也不需要存儲結(jié)構(gòu)化數(shù)據(jù)。在開發(fā)一款網(wǎng)絡(luò)連接的應(yīng)用程序時,它遲早會需要使用一些JSON。
這里簡單介紹一下JSON在flutter中的使用。
Tips:
編碼和序列化是將數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為字符串的同一件事。解碼和反序列化是將字符串轉(zhuǎn)換為數(shù)據(jù)結(jié)構(gòu)的相反過程。然而,序列化通常也指將數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為更易于閱讀的格式的整個過程。
哪種JSON序列化方法適合
這里主要簡單介紹兩種序列化方式:
- 手動序列化
- 使用代碼自動序列化
不同的項目復(fù)雜度以及用例都不同,對于一些較小的項目或者類似原型的的應(yīng)用,使用代碼生成可能有些大材小用,而對于有很多不同json模型的應(yīng)用程序,使用手動序列化則除了無聊之外,有可能會產(chǎn)生不必要的問題和麻煩。
手動進行序列化
手動進行json解碼說的是使用dart:convert內(nèi)置的json解碼器,通過將原始的json數(shù)據(jù)傳遞給jsonDecode()方法,然后在返回的Map<String, dynamic>這個類型的數(shù)據(jù)中我們可以找到我們想用的數(shù)據(jù)。不需要別的依賴和其他的設(shè)置過程,對于驗證一些快速的原型或者小型的項目非常有效。
當(dāng)項目逐漸變的越來越大的時候,手動解碼可能會表現(xiàn)的不盡人意。手動編寫解碼邏輯可能會變得越來越難以管理,而且變得非常容易出錯,如果訪問到不存在的字段,或者編寫時有拼寫錯誤,代碼在運行時就會發(fā)生錯誤。
使用代碼自動序列化
對于中大型項目來說,使用代碼自動進行序列化可能會是一個比較不錯的選擇,意味著我們可以使用外部的依賴庫來生成我們想要的模版。我們通過設(shè)置一些初始化的配置,然后運行一個file watcher從我們的模型類中生成我們想要的代碼數(shù)據(jù)。
比如我們可以使用:json_serializable或者build_value諸如之類的庫。
這種方法適用于更大的項目。不需要手工編寫模版,并且在編譯時會捕捉到訪問JSON字段時的拼寫錯誤。
代碼生成的缺點是需要一些初始設(shè)置。另外,生成的源文件可能會在項目導(dǎo)航器中產(chǎn)生視覺上的混亂。
Flutter 中是否有 GSON/Jackson/Moshi 之類的序列化類庫?
GSON以及Jackson都是 Java中用來序列化json的類庫。
Moshi則是Kotlin中用來序列化json的類庫。
事實上Flutter中并沒有類似的庫。
因為,這樣的庫需要使用運行時反射,這在Flutter中是禁用的。運行時反射會干擾【樹抖動】treeShaking,Dart已經(jīng)支持了很長時間。通過treeShaking樹抖動,您可以從發(fā)布版本中“抖掉”未使用的代碼,這可以優(yōu)化應(yīng)用程序的大小。
由于反射默認情況下會隱式使用所有代碼,因此很難進行treeShaking樹抖動。這些工具無法知道哪些部分在運行時未使用,因此冗余代碼很難去除。使用反射時,無法輕松優(yōu)化應(yīng)用程序大小。
雖然我們不能在Flutter中使用運行時反射,但有些庫提供了類似的API,是基于代碼生成。
使用dart:convert內(nèi)置庫手動進行序列化
Flutter中的基本JSON序列化非常簡單。Flutter有一個內(nèi)置的dart:convert庫,其中包含一個簡單的JSON編碼器和解碼器。
看下面的示例:
{
"name": "John Smith",
"email": "john@example.com"
}
使用dart:convert庫,我們有兩種方法進行序列化。
調(diào)用jsonDecode()方法:
Map<String, dynamic> user = jsonDecode(jsonString);
print('Howdy, ${user['name']}!');
print('We sent the verification link to ${user['email']}.');
但是需要注意的是,jsonDecode()方法會返回一個類型為Map<String, dynamic>的類型,這樣的話,我們就特別需要注意json中字段的各種類型。
在模型類中序列化JSON
此外,我們可以引入一個簡單的模型類(在本例中稱為User)來解決前面提到的問題。在User類中,我們可以發(fā)現(xiàn):
User.fromJson()構(gòu)造函數(shù),用于從Map構(gòu)造新的User實例。toJson()方法,將User實例轉(zhuǎn)換為Map。
使用這種方法,調(diào)用代碼時可以具有類型安全及編譯時異常提醒。如果我們輸入了錯別字,或者將字段視為int而不是String,應(yīng)用程序?qū)⒉粫幾g,而不會在運行時崩潰。
// 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)在轉(zhuǎn)移到模型本身內(nèi)部。使用這種新方法,您可以輕松地解碼User:
Map<String, dynamic> userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
print('Howdy, ${user.name}!');
print('We sent the verification link to ${user.email}.');
和解碼(Decode)相反的是編碼(Encode),如果我們想要對User進行編碼,我們可以使用jsonEncode()方法:
String json = jsonEncode(user);
使用這種方法,調(diào)用代碼根本不必擔(dān)心JSON序列化。然而,模型類仍然必須這樣做。在生產(chǎn)應(yīng)用程序中,我們需要確保序列化工作正常進行。在實際開發(fā)過程中,User.fromJson()和User.toJson()方法可能都需要進行單元測試以保證結(jié)果的正確性。
使用序列化庫
盡管有其他庫可用,但是這里使用了json_serializable,這是一個自動源代碼生成器,可為我們生成json序列化模版。
要在項目中包含json_serializable,需要一個常規(guī)依賴項和兩個開發(fā)依賴項。簡而言之,開發(fā)依賴項是不包含在我們的應(yīng)用程序源代碼中的依賴項,它們只在開發(fā)環(huán)境中使用。
我們需要在pubspec.yaml進行如下配置:
**pubspec.yaml** dependencies: # Your other regular dependencies here json_annotation: <latest_version> dev_dependencies: # Your other dev_dependencies here build_runner: <latest_version> json_serializable: <latest_version>
然后在項目根文件夾中運行flutter pub-get以安裝依賴。
然后我們以json_serializable的方式創(chuàng)建模型類:
// user.dart
import 'package:json_annotation/json_annotation.dart';
/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';
/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
@JsonSerializable()
class User {
User(this.name, this.email);
String name;
String email;
/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case, User.
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$UserToJson(this);
}
通過這種設(shè)置,源代碼生成器生成用于對JSON中的name和email字段進行編碼和解碼的代碼。
如果需要的話,我們還可以定制命名策略,比如,如果API返回帶有的對象帶有snake_case屬性,并且我們希望在模型中使用lowerCamelCase,則可以使用帶有name參數(shù)的@JsonKey注釋:
/// Tell json_serializable that "registration_date_millis" should be /// mapped to this property. @JsonKey(name: 'registration_date_millis') final int registrationDateMillis;
服務(wù)器和客戶端最好都遵循相同的命名策略。
@JsonSerializable()提供了fieldRename的枚舉,用于將dart字段完全轉(zhuǎn)換為JSON鍵。
修改@JsonSerializable(fieldRename:fieldRename.sake)相當(dāng)于向每個字段添加@JsonKey(name:“<snake_case>”)。
服務(wù)器返回的數(shù)據(jù)是不確定的,所以有必要驗證和保護客戶端上的數(shù)據(jù)。
其他常用的@JsonKey注釋包括:
/// Tell json_serializable to use "defaultValue" if the JSON doesn't /// contain this key or if the value is `null`. @JsonKey(defaultValue: false) final bool isAdult; /// When `true` tell json_serializable that JSON must contain the key, /// If the key doesn't exist, an exception is thrown. @JsonKey(required: true) final String id; /// When `true` tell json_serializable that generated code should /// ignore this field completely. @JsonKey(ignore: true) final String verificationCode;
運行代碼生成實用程序
當(dāng)?shù)谝淮蝿?chuàng)建json_serializable類時,會出現(xiàn)類似下圖所示的錯誤。

這些錯誤完全是正常的,只是因為為模型類生成的代碼還不存在。要解決此問題,我們需要運行生成序列化樣板的代碼生成器。
運行代碼生成器有兩種方法。
- 一次性代碼生成
- 持續(xù)生成代碼
一次性代碼生成
通過在項目根目錄中運行
flutter pub run build_runner build --delete-conflicting-outputs
我們可以在需要時為模型生成JSON序列化代碼。這將觸發(fā)一次性構(gòu)建,該構(gòu)建將遍歷源文件,選擇相關(guān)文件,并為它們生成必要的序列化代碼。
雖然這很方便,但如果我們不必每次在模型類中進行更改時都手動運行構(gòu)建,那就更好了。
持續(xù)生成代碼
觀察者模式使我們的源代碼生成過程更加方便。它監(jiān)聽項目文件中的更改,并在需要時自動生成必要的文件。 通過在項目根目錄中運行
flutter pub run build_runner watch --delete-conflicting-outputs
可以安全地啟動一次觀察程序,并讓它在一直后臺運行。
使用json_serializable模型
要以JSON_serializable的方式解碼JSON字符串,實際上不需要對我們之前的代碼進行任何更改。
Map<String, dynamic> userMap = jsonDecode(jsonString); var user = User.fromJson(userMap);
編碼也是如此。調(diào)用API與之前相同。
String json = jsonEncode(user);
使用json_serializable,我們可以放棄User類中的任何手動json序列化。源代碼生成器創(chuàng)建一個名為user.g.dart的文件,該文件具有所有必要的序列化邏輯。我們不再需要編寫自動化測試來確保序列化工作,現(xiàn)在庫負責(zé)確保序列化工作正常。
ps:這里所說的解碼和編碼,對應(yīng)的是Decode和Encode。
以上就是flutter中的JSON和序列化方法及使用詳解的詳細內(nèi)容,更多關(guān)于flutter JSON序列化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android編程圖片加載類ImageLoader定義與用法實例分析
這篇文章主要介紹了Android編程圖片加載類ImageLoader定義與用法,結(jié)合實例形式分析了Android圖片加載類ImageLoader的功能、定義、使用方法及相關(guān)操作注意事項,代碼中備有較為詳盡的注釋便于理解,需要的朋友可以參考下2017-12-12
Flutter使用JsBridge方式處理Webview與H5通信的方法
這篇文章主要介紹了Flutter使用JsBridge方式處理Webview與H5通信的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04
Android使用RecycleView實現(xiàn)拖拽交換item位置
這篇文章主要為大家詳細介紹了Android使用RecycleView實現(xiàn)拖拽交換item位置,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
RxJava 1升級到RxJava 2過程中踩過的一些“坑”
RxJava2相比RxJava1,它的改動還是很大的,那么下面這篇文章主要給大家總結(jié)了在RxJava 1升級到RxJava 2過程中踩過的一些“坑”,文中介紹的非常詳細,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下來要一起看看吧。2017-05-05
Android自定義View實現(xiàn)柱狀波形圖的繪制
柱狀波形圖是一種常見的圖形。一個個柱子按順序排列,構(gòu)成一個波形圖。本文將利用Android自定義View實現(xiàn)柱狀波形圖的繪制,需要的可以參考一下2022-08-08
Android使用 Spinner控件實現(xiàn)下拉框功能
Spinner是android的一種控件,用它我們可以實現(xiàn)下拉框。下面通過實例代碼給大家介紹Android使用 Spinner控件實現(xiàn)下拉框功能,感興趣的朋友一起看看吧2018-08-08
Android Internet應(yīng)用實現(xiàn)獲取天氣預(yù)報的示例代碼
這篇文章主要介紹了Android網(wǎng)絡(luò)編程及Internet應(yīng)用-獲取天氣,小編覺得挺不錯的,一起跟隨小編過來看看吧2018-05-05
Android開發(fā)之DialogFragment用法實例總結(jié)
這篇文章主要介紹了Android開發(fā)之DialogFragment用法,結(jié)合實例形式總結(jié)分析了Android使用DialogFragment代替Dialog功能的相關(guān)使用技巧與注意事項,需要的朋友可以參考下2017-11-11

