一移国、FlutterJsonBeanFactory
Flutter 使用的是 Dart 語(yǔ)言進(jìn)行開(kāi)發(fā)召庞,而 Dart 語(yǔ)言沒(méi)有反射,所以無(wú)法像 Java 一樣通過(guò)反射直接將 Json 數(shù)據(jù)映射為對(duì)應(yīng)的對(duì)象實(shí)體類(lèi)對(duì)象。官方解決方案是將 Json 數(shù)據(jù)轉(zhuǎn)換為字典构蹬,然后從字典中進(jìn)行取數(shù)使用惋鹅。但直接從字典中取數(shù)很不方便则酝,寫(xiě)代碼時(shí)沒(méi)有自動(dòng)提示很不友好,而且可能在寫(xiě)的時(shí)候?qū)戝e(cuò)字段名闰集。
我們可以將 Json 轉(zhuǎn)換為字典后再映射到對(duì)象實(shí)體字段里沽讹,這樣使用時(shí)就可以直接使用對(duì)應(yīng)實(shí)體類(lèi)對(duì)象般卑。而FlutterJsonBean就可以幫我們自動(dòng)生成映射代碼。
二爽雄、插件安裝
插件市場(chǎng)搜索FlutterJsonBeanFactory蝠检,安轉(zhuǎn)后重啟AS。我安裝的版本是4.4.5挚瘟,實(shí)例代碼也是基于此版本的分析叹谁。
并且可以在Setting->Tools->FlutterJsonBeanFactory里邊自定義實(shí)體類(lèi)的后綴,默認(rèn)是entity乘盖。
三焰檩、創(chuàng)建實(shí)體類(lèi)
復(fù)制json到粘貼板,右鍵自己要存放實(shí)體的目錄订框,可以看到JsonToDartBeanAction
Class Name
是實(shí)體名字析苫,會(huì)默認(rèn)加上entity
JSON Text
Json文本
null-able
勾選后所有屬性都是可空的?
,不勾選都會(huì)加上late,延遲初始化
執(zhí)行Make后生成代碼目錄如下:
models
項(xiàng)目自建布蔗,存放實(shí)體
generated/json
是插件生成目錄藤违,xx_entity.g.daet
是實(shí)體類(lèi)生成的輔助類(lèi)方法, base
是存放基礎(chǔ)公共代碼
1.xx_entity.dart
@JsonSerializable()
class UserEntity {
String? id;
String? name;
String? age;
UserEntity();
factory UserEntity.fromJson(Map<String, dynamic> json) =>
$UserEntityFromJson(json);
Map<String, dynamic> toJson() => $UserEntityToJson(this);
@override
String toString() {
return jsonEncode(this);
}
}
會(huì)生成fromJson
的工廠(chǎng)方法和toJson
方法纵揍,分別調(diào)用xx_entity.g.dart
中的$xxEntityFromJson
方法和$xxEntityToJson
方法顿乒。toSring
方法會(huì)把對(duì)象轉(zhuǎn)json字符串顯示。
如果是修改了實(shí)體類(lèi)泽谨,鼠標(biāo)懸停在gernerated目錄上璧榄,執(zhí)行Alt+J快捷鍵,就會(huì)自動(dòng)生成新的映射代碼吧雹,并且去除多余的骨杂。
2.xx_entity.g.dart
實(shí)體類(lèi)對(duì)應(yīng)的輔助方法文件,g.dart
是文件的后綴雄卷,存放在generated/json
目錄下搓蚪。
主要包含$xxEntityFromJson
和$xxEntityToJson
方法,$
+實(shí)體類(lèi)名
為前綴丁鹉。
$xxFromJson
將 Json 數(shù)據(jù)的對(duì)應(yīng)字段取出來(lái)然后賦值給實(shí)體類(lèi)的對(duì)應(yīng)字段妒潭。Json 數(shù)據(jù)轉(zhuǎn)換為實(shí)體字段使用了 jsonConvert.convert 其定義在 json_convert_content.dart 中。
$xxToJson
將實(shí)體數(shù)據(jù)轉(zhuǎn)換為 Map 字典揣钦。
UserEntity $UserEntityFromJson(Map<String, dynamic> json) {
final UserEntity userEntity = UserEntity();
final String? id = jsonConvert.convert<String>(json['id']);
if (id != null) {
userEntity.id = id;
}
final String? user = jsonConvert.convert<String>(json['user']);
if (user != null) {
userEntity.user = user;
}
final String? age = jsonConvert.convert<String>(json['age']);
if (age != null) {
userEntity.age = age;
}
final int? sex = jsonConvert.convert<int>(json['sex']);
if (sex != null) {
userEntity.sex = sex;
}
return userEntity;
}
Map<String, dynamic> $UserEntityToJson(UserEntity entity) {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = entity.id;
data['user'] = entity.user;
data['age'] = entity.age;
data['sex'] = entity.sex;
return data;
}
3.json_convert_content.dart
json_convert_content.dart
為JsonConvert
類(lèi)雳灾, 用于統(tǒng)一進(jìn)行 Json 與實(shí)體類(lèi)的轉(zhuǎn)換。
JsonConvert jsonConvert = JsonConvert();
typedef JsonConvertFunction<T> = T Function(Map<String, dynamic> json);
class JsonConvert {
static final Map<String, JsonConvertFunction> _convertFuncMap = {
(UserEntity).toString(): UserEntity.fromJson,
};
T? convert<T>(dynamic value) {...}
List<T?>? convertList<T>(List<dynamic>? value) {...}
List<T>? convertListNotNull<T>(dynamic value) {...}
T? asT<T extends Object?>(dynamic value) {...}
//list is returned by type
static M? _getListChildType<M>(List<Map<String, dynamic>> data) {...}
static M? fromJsonAsT<M>(dynamic json) {...}
}
convert
將json數(shù)據(jù)轉(zhuǎn)換為實(shí)體對(duì)象冯凹。首先判斷了傳入的數(shù)據(jù)是否為null
谎亩,為null
則直接返回null
, 不為空則調(diào)用asT
方法。在生成的.g.dart
的$xxEntityFromJson
方法中非 List 類(lèi)型字段基本都是調(diào)用 convert 方法進(jìn)行轉(zhuǎn)換匈庭。
T? convert<T>(dynamic value) {
if (value == null) {
return null;
}
return asT<T>(value);
}
convertList
將json數(shù)據(jù)轉(zhuǎn)換為實(shí)體對(duì)象List夫凸。首先也是判斷了傳入的數(shù)據(jù)是否為null
,為null
則直接返回null
嚎花, 不為空則遍歷value
使用map
調(diào)用asT
方法進(jìn)行轉(zhuǎn)換寸痢,最終還是調(diào)用的asT
方法。在轉(zhuǎn)換上加了try-catch
,如果報(bào)錯(cuò)則返回空的List
紊选。
List<T?>? convertList<T>(List<dynamic>? value) {
if (value == null) {
return null;
}
try {
return value.map((dynamic e) => asT<T>(e)).toList();
} catch (e, stackTrace) {
debugPrint('asT<$T> $e $stackTrace');
return <T>[];
}
}
converListNotNull
與 convertList
的區(qū)別是參數(shù)不一樣,convertList
參數(shù)傳入的是List<dynamic>
而convertListNotNull
傳入的直接是dynamic
啼止。其次最大的區(qū)別是調(diào)用asT
方法時(shí)convertListNotNull
在 asT 后面加了一個(gè) !
,表示不為空兵罢。
當(dāng)在實(shí)體類(lèi)里定義字段為List
類(lèi)型時(shí)献烦,會(huì)根據(jù)是否為List
中元素的非空類(lèi)型而選擇生成 convertList
或 convertListNotNull
來(lái)進(jìn)行轉(zhuǎn)換,非空采用convertListNotNull
卖词,可空采用convertList
-
List<String?>? foodList1;
采用convertList
-
List<String>? foodList2;
采用convertListNotNull
-
late List<String?> foodList3;
采用convertList
-
late List<String> foodList4;
采用convertListNotNull
List<T>? convertListNotNull<T>(dynamic value) {
if (value == null) {
return null;
}
try {
return (value as List<dynamic>).map((dynamic e) => asT<T>(e)!).toList();
} catch (e, stackTrace) {
debugPrint('asT<$T> $e $stackTrace');
return <T>[];
}
}
as<T>
首先判斷傳入的數(shù)據(jù)類(lèi)型是否為要轉(zhuǎn)換的數(shù)據(jù)類(lèi)型巩那,如果是的話(huà)就直接返回傳入?yún)?shù),即如果要將傳入數(shù)據(jù)轉(zhuǎn)換為User
此蜈,但是傳入?yún)?shù)本身就是User
類(lèi)型即横,那就直接返回。
然后通過(guò) T.String()
獲取泛型類(lèi)型的名稱(chēng)裆赵,再與String
东囚、int
、double
战授、DateTime
页藻、bool
這些基礎(chǔ)數(shù)據(jù)類(lèi)型進(jìn)行比較,如果是這些類(lèi)型則調(diào)用這些類(lèi)型的轉(zhuǎn)換方法進(jìn)行轉(zhuǎn)換植兰。
最后份帐,如果不是基礎(chǔ)類(lèi)型則去_convertFuncMap
尋找其它的實(shí)體類(lèi)型,并調(diào)用其value楣导,即fromJson方法废境,類(lèi)似遞歸的去解析。
T? asT<T extends Object?>(dynamic value) {
if (value is T) {
return value;
}
final String type = T.toString();
try {
final String valueS = value.toString();
if (type == "String") {
return valueS as T;
} else if (type == "int") {
final int? intValue = int.tryParse(valueS);
if (intValue == null) {
return double.tryParse(valueS)?.toInt() as T?;
} else {
return intValue as T;
}
} else if (type == "double") {
return double.parse(valueS) as T;
} else if (type == "DateTime") {
return DateTime.parse(valueS) as T;
} else if (type == "bool") {
if (valueS == '0' || valueS == '1') {
return (valueS == '1') as T;
}
return (valueS == 'true') as T;
} else if (type == "Map" || type.startsWith("Map<")) {
return value as T;
} else {
if (_convertFuncMap.containsKey(type)) {
return _convertFuncMap[type]!(value) as T;
} else {
throw UnimplementedError('$type unimplemented');
}
}
} catch (e, stackTrace) {
debugPrint('asT<$T> $e $stackTrace');
return null;
}
}
fromJsonAsT
判斷傳入Json
數(shù)據(jù)是否為null
筒繁,為null
則直接返回null
彬坏。然后判斷Json
數(shù)據(jù)是否為List
,是 List
則調(diào)用_getListChildType
否則通過(guò)全局變量jsonConvert
調(diào)用asT
static M? fromJsonAsT<M>(dynamic json) {
if (json is List) {
return _getListChildType<M>(
json.map((e) => e as Map<String, dynamic>).toList());
} else {
return jsonConvert.asT<M>(json);
}
}
_getListChildType
<UserEntity>[] is M
直接創(chuàng)建對(duì)應(yīng)實(shí)體類(lèi)的空 List 判斷是否為泛型類(lèi)型膝晾,如果類(lèi)型相同,則通過(guò)map
調(diào)用對(duì)應(yīng)實(shí)體類(lèi)的 fromJson
方法進(jìn)行轉(zhuǎn)換.
static M? _getListChildType<M>(List<Map<String, dynamic>> data) {
if (<UserEntity>[] is M) {
return data
.map<UserEntity>((Map<String, dynamic> e) => UserEntity.fromJson(e))
.toList() as M;
}
debugPrint("${M.toString()} not found");
return null;
}
4.json_field.dart
JsonSerializable
類(lèi)注解务冕,二次生成代碼時(shí)插件查找該注解的類(lèi)進(jìn)行生成血当。
JSONField
字段注解,用于自定義字段映射和配置是否序列化和反序列化字段。
class JsonSerializable{
const JsonSerializable();
}
class JSONField {
//Specify the parse field name
final String? name;
//Whether to participate in toJson
final bool? serialize;
//Whether to participate in fromMap
final bool? deserialize;
const JSONField({this.name, this.serialize, this.deserialize});
}
四臊旭、使用
1.單實(shí)體解析
直接調(diào)用實(shí)體類(lèi)對(duì)應(yīng)的``fromJson```方法即可將 Json 數(shù)據(jù)解析為實(shí)體對(duì)象落恼。
UserEntity? user;
String userData = """
{
"id":"1",
"name":"qi",
"age":22
}
""";
user = UserEntity.fromJson(jsonDecode(userData));
調(diào)用生成的JsonConvert去解析,使用convert
、asT
离熏、fromJsonAsT
都能得到結(jié)果
user = jsonConvert.convert<UserEntity>(jsonDecode(userData));
user = jsonConvert.asT<UserEntity>(jsonDecode(userData));
user = JsonConvert.fromJsonAsT<UserEntity>(jsonDecode(userData));
2.List解析
解析 Json List 數(shù)據(jù)則需要調(diào)用 JsonConvert
的對(duì)應(yīng)方法進(jìn)行解析佳谦,除了使用上面的 convert
、asT
滋戳、fromJsonAsT
外钻蔑,還可以使用 convertList
、convertListNotNull
List<UserEntity>? users;
List<UserEntity?>? userNulls;
users = jsonConvert.convert<List<UserEntity>>(jsonDecode(userData));
users = jsonConvert.asT<List<UserEntity>>(jsonDecode(userData));
users = JsonConvert.fromJsonAsT<List<UserEntity>>(jsonDecode(userData));
users = jsonConvert.convertListNotNull<UserEntity>(jsonDecode(userData));
userNulls = jsonConvert.convertList<UserEntity>(jsonDecode(userData));
convertList
奸鸯、convertListNotNull
與 convert
咪笑、asT
、fromJsonAsT
的區(qū)別在于前者的泛型為 List Item元素的泛型類(lèi)型娄涩,后者則直接為對(duì)應(yīng) List 的類(lèi)型窗怒。如上面 convertList
、convertListNotNull
的泛型直接為UserEntity
, 而 convert
蓄拣、asT
扬虚、fromJsonAsT
的泛型為List<UserEntity>
。
3.JSONField的使用
自定義字段名
處理Json數(shù)據(jù)字段和實(shí)體屬性字段不一致的問(wèn)題球恤,如后臺(tái)返回Json命名不規(guī)范這種情況辜昵。可以用JSONField
自定義字段映射碎捺。如后臺(tái)返回 AGE
就可以如下使用路鹰,映射成age
。加完之后照樣需要執(zhí)行Alt
+J
@JSONField(name: "AGE")
String? age;
忽略字段
JSONField 還有兩個(gè)字段 serialize
收厨、deserialize
用于序列化和反序列化時(shí)忽略某個(gè)字段晋柱。
五、優(yōu)化
后臺(tái)返回的數(shù)據(jù)一般是經(jīng)過(guò)一層包裝
{
"code": 200,
"message": "success",
"data":{
"id": "1",
"name": "qi1",
"age": 18
}
}
而重新用插件生成會(huì)生成如下代碼:
@JsonSerializable()
class ApiResponseEntity {
int? code;
String? message;
ApiResponseData? data;
ApiResponseEntity();
factory ApiResponseEntity.fromJson(Map<String, dynamic> json) => $ApiResponseEntityFromJson(json);
Map<String, dynamic> toJson() => $ApiResponseEntityToJson(this);
@override
String toString() {
return jsonEncode(this);
}
}
@JsonSerializable()
class ApiResponseData {
String? id;
String? name;
int? age;
ApiResponseData();
factory ApiResponseData.fromJson(Map<String, dynamic> json) => $ApiResponseDataFromJson(json);
Map<String, dynamic> toJson() => $ApiResponseDataToJson(this);
@override
String toString() {
return jsonEncode(this);
}
}
要死這樣诵叁,每一個(gè)接口的都有一個(gè)ResponseEntity
雁竞,使用起來(lái)不便于統(tǒng)一封裝。
所以我們可以把ApiResponseData
換成 dynamic
拧额,文件底部的ApiResponseData
信息也全部刪除碑诉,再執(zhí)行Alt
+J
,這樣就會(huì)自動(dòng)清理掉整理json_convert_content.dart
和api_response_entity.g.dart
中的ApiResponseData
痕跡侥锦。再把dynamic
替換成T
,并且去除頂部的@JsonSerializable()
进栽,避免下次執(zhí)行Alt
+J
,替換掉自己的自定義恭垦。
@JsonSerializable()
class ApiResponseEntity<T> {
late int code;
late String message;
late T data;
ApiResponseEntity();
factory ApiResponseEntity.fromJson(Map<String, dynamic> json) =>
$ApiResponseEntityFromJson<T>(json);
Map<String, dynamic> toJson() => $ApiResponseEntityToJson(this);
@override
String toString() {
return jsonEncode(this);
}
}
ApiResponseEntity<T> $ApiResponseEntityFromJson<T>(Map<String, dynamic> json) {
final ApiResponseEntity<T> apiResponseEntity = ApiResponseEntity<T>();
final int? code = jsonConvert.convert<int>(json['code']);
if (code != null) {
apiResponseEntity.code = code;
}
final String? message = jsonConvert.convert<String>(json['message']);
if (message != null) {
apiResponseEntity.message = message;
}
final T data = jsonConvert.convert<dynamic>(json['data']);
if (data != null) {
apiResponseEntity.data = data;
}
return apiResponseEntity;
}
Map<String, dynamic> $ApiResponseEntityToJson(ApiResponseEntity entity) {
final Map<String, dynamic> data = <String, dynamic>{};
data['code'] = entity.code;
data['message'] = entity.message;
data['data'] = entity.data;
return data;
}
并且把api_response_entity.g.dart
移除generated
目錄快毛,因?yàn)槟莻€(gè)目錄會(huì)自動(dòng)刪除無(wú)用的文件格嗅。可以和api_reponse_entity.dart
單獨(dú)存放在一個(gè)文件夾當(dāng)中唠帝。
優(yōu)化后使用
第一次發(fā)現(xiàn)屯掖,reponse的data是null。因?yàn)樾碌牟寮?asT
方法沒(méi)有去調(diào)用fromJsonAsT
襟衰,這個(gè)需要我們自加上贴铜,否則會(huì)失敗。
if (_convertFuncMap.containsKey(type)) {
return _convertFuncMap[type]!(value) as T;
} else {
return fromJsonAsT<T>(value);
// throw UnimplementedError('$type unimplemented');
}
//單實(shí)體
String responseData1 = """
{
"code": 200,
"message": "success",
"data":{
"id": 1,
"name": "qi1",
"age": 21
}
}
""";
//List
String responseData2 = """
{
"code": 200,
"message": "success",
"data":[
{
"id": 1,
"name": "qi1",
"age": 21
},{
"id": 2,
"name": "qi2",
"age": 22
}
]
}
""";
//基礎(chǔ)數(shù)據(jù)類(lèi)型
String responseData3 = """
{
"code": 200,
"message": "success",
"data": 18
}
""";
_apiResponseDecode() {
setState(() {
response1 = ApiResponseEntity.fromJson(jsonDecode(responseData1));
response2 = ApiResponseEntity.fromJson(jsonDecode(responseData2));
response3 = ApiResponseEntity.fromJson(jsonDecode(responseData3));
});
}
_getApiResponseContent() {
return response1.toString() +
"\n" +
response2.toString() +
"\n" +
response3.toString();
}