如今很難想象移動應用程序不須要與後臺交互或者存儲結構化數據。如今開發,數據傳輸方式基本都是用JSON
,在Flutter
中是沒有GSON/Jackson/Moshi
這些庫,由於這些庫須要運行時反射,在Flutter
是禁用的。運行時反射會干擾Dart
的_tree shaking_。使用_tree shaking_,能夠在發版是"去除"未使用的代碼,來優化軟件的大小。因爲反射會默認使用全部代碼,所以_tree shaking_會很難工做,這些工具沒法知道哪些widget
在運行時未被使用,所以冗餘代碼很難剝離,使用反射時,應用尺寸沒法輕鬆進行優化,雖然不能在Flutter
使用運行時反射,但有些庫提供了類型簡單易用的API
,但它們是基於代碼生成的。下面學學在Flutter
中如何操做JSON
數據的使用JSON
有兩個常規策略:json
JSON model
的複雜應用程序,手動序列化可能會比較繁瑣,且容易出錯。Flutter
中基本的JSON序列化很是簡單,Flutter
有一個內置的dart:convert
庫,其中包含一個簡單的JSON解碼器和編碼器。下面簡單實現一下:安全
首先記得導庫:函數
import 'dart:convert';
而後根據字符串解析:工具
//內連序列化JSON decodeJson() { var data= '{"name": "Knight","email": "Knight@163.com"}'; Map<String,dynamic> user = json.decode(data); //輸出名字 print("Hello,my name is ${user['name']}"); //輸出郵箱 print("Hello,This is my email ${user['email']}"); }
結果輸出:優化
I/flutter ( 5866): Hello,my name is Knight
I/flutter ( 5866): Hello,This is my email Knight@163.com
這樣,能夠得到咱們想要的數據了,我以爲這種方法很實用又能簡單理解,可是不幸的是,JSON.decode()
僅返回一個Map<String,dynamci>
,這意味着當直到運行才知道值的類型,這種方法會失去大部分靜態類型語言特性:類型安全、自動補全和編譯時異常。這樣的話,代碼變得很是容易出錯,就好像上面咱們訪問name
字段,打字打錯了,達成namr
。可是這個JSON
在map結構中,編譯器不知道這個錯誤的字段名(編譯時不會報錯)。爲了解決所說的問題,模型類中序列化JSON的做用出來了。ui
經過引入一個簡單的模型類(model class)來解決前面提到的問題,創建一個User
類,在類內部有兩個方法:this
User.fromJson
構造函數,用於從一個map構造出一個User
實例map structuretoJson
方法,將User
實例化一個map 這樣調用的代碼就具備類型安全、自動補全和編譯時異常,當拼寫錯誤或字段類型視爲其餘類型,程序不會經過編譯,那就避免運行時崩潰。新建一個model文件夾,用來放實體,在其文件下新建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, }; }
調用以下:spa
import 'model/User.dart';//記得添加 .... //使用模型類反序列化 decodeModelJson(){ var data= '{"name": "Knight","email": "Knight@163.com"}'; Map userMap = json.decode(data); var user = new User.fromJson(userMap); //打印出名字 print("Hello,my name is ${user.name}"); //打印出郵箱 print("Hello,my name is ${user.email}"); }
把序列化邏輯到移到模型自己內部,採用這種方法,反序列化數據就很簡單了。序列化一個user,只是將User
對象傳遞給該JSON.encode
方法:命令行
//序列化一個user encodeModelJson(){ var user = new User("Knight","Knight163.com"); String user_json = json.encode(user); print(user_json); }
結果輸出:
I/flutter ( 6684): {"name":"Knight","email":"Knight163.com"}
下面使用json_serializable package
包,它是一個自動化的源代碼生成器,能夠爲開發者生成JSON序列化魔板。
要包含json_serializable
到項目中,須要一個常規和兩個開發依賴項,開發依賴項是不包含在應用程序源代碼中的依賴項:
dependencies: # Your other regular dependencies here json_annotation: ^2.0.0 dev_dependencies:-->開發依賴項 # Your other dev_dependencies here build_runner: ^1.1.3 -->最新版本1.2.8 由於我sdk版本比較低 因此用低版本 json_serializable: ^2.0.2
有兩種運行代碼生成器的方法:
flutter packages pub run build_runner build
,能夠在須要爲咱們的model
生成json
序列化代碼。這觸發一次性構建,它經過源文件,挑選相關的併爲它們生成必要的序列化代碼。這個很是方便,可是若是咱們不須要每次在model類中進行更改都要手動運行構建命令的話會更好。flutter packages pub run build_runner watch
在項目根目錄運行啓動_watcher_,只需啓動一次觀察器,而後並讓它在後臺運行,這是安全的。將上面的User.dart
修改爲下面:
import 'package:json_annotation/json_annotation.dart'; part 'User.g.dart';-->一開始爆紅 //這個標註是告訴生成器,這個類是須要生成Model類的 @JsonSerializable() class User{ User(this.name, this.email); String name; String email; factory User.fromJson(Map<String, dynamic> json){--->一開始爆紅 return _$UserFromJson(json); } Map<String, dynamic> toJson() { --->一開始爆紅 return _$UserToJson(this); } }
下面就用一次性生成命令,在項目根目錄打開命令行執行:
最後發現會在當前目錄生成User.g.dart
文件:
裏面的內容能夠本身去看看看,就是反序列化/序列化的操做。注意:沒生成User.g.dart
執行多幾回命令便可。最後經過json_serializable
方式反序列化JSON
字符串,不須要對先前代碼修改:
var data= '{"name": "Knight","email": "Knight@163.com"}'; Map userMap = json.decode(data); var user = new User.fromJson(userMap); //打印出名字 print("Hello,my name is ${user.name}"); //打印出郵箱 print("Hello,my name is ${user.email}");
var user = new User("Knight","Knight163.com"); String user_json = json.encode(user); print(user_json);
結果是跟上面同樣,不過這種方式額外多了生成一個文件...