依賴:xxf_json
反序列化兼容特征一覽表
類型\是否兼容 | int | double | num | string | bool |
---|---|---|---|---|---|
int | yes | yes | yes | yes | yes |
double | yes | yes | yes | yes | yes |
num | yes | yes | yes | yes | yes |
string | yes | yes | yes | yes | yes |
bool | yes | yes | yes | yes | yes |
專業詞語
.g.dart : 是json_annotation生成的中間解析文件
DTO : 網絡傳輸模型,這里泛指json解析模型
中間件
對json提供如下基礎中間件,兩種兼容模式,一種給全日志
-
JsonConverter 兼容基本類型(int,num,double,string,bool),異常情況解析成對應類型的默認值,達到同級別js,oc等語言層兼容
-
nullable_converter 兼容基本類型(int,num,double,string,bool),異常情況解析成null,需要DTO聲明字段為可空類型
-
strict_converter 先兼容解析,解析不了再報錯,解決json解析報錯,不提示具體內容,導致一個一個去比較DTO里聲明的字段,或者打印stack才可以排查具體字段
至于要使用多少類型兼容和什么策略,請自己選,上圖只是模版
用法
-
DTO層增加注解 通過 @JsonSerializable 注解參數converters 注入!
其中 primitiveConvertors :基本類型解析器,安全處理,如遇到失敗會轉換成默認值 primitiveNullableConvertors:基本類型解析器,安全處理,如遇到失敗變成null,適合聲明可空類型的字段
primitiveStrictConvertors:基本類型解析器,不安全處理,用于提示內容,比原錯誤始信息增加 內容本身到日志里面,解決原始報錯,不提示具體內容,不好分析DTO的那個字段
用法代碼示例:
@JsonSerializable(converters: [...primitiveNullableConvertors, RRuleJsonAdapter()])
class Event{
}
-
字段級別增加注解
通過 @JsonKey fromJson 注解增加參數
用法代碼示例:
@JsonSerializable()
class Event {//類型不對,就解析成""雙引號字符串@JsonKey(fromJson: StringDecoder.decodeOrEmpty)String? eventId; //設備內日程id
}
提供更自由的控制,每種類型都提供三種策略
bool_decoder.dart
double_decoder.dart
int_decoder.dart
num_decoder.dart
string_decoder.dart
class StringDecoder {///策略1 異常情況解析為空static String? decodeOrNull(dynamic json) {if (json == null) return null;return json.toString();}///策略2 異常情況解析默認值,比@JsonKey(defaultValue: "")更健壯static String decodeOrEmpty(dynamic value) {return decodeOrNull(value) ?? "";}///策略3 嘗試解析異常情況報錯并給出錯誤值static String decodeOrException(dynamic value) {return decodeOrNull(value) ??(throw BaseDecoder.createParseError("String", value));}
}
框架的中間件優先級是 @JsonKey(fromJson)>@JsonSerializable(converters)
推薦傾向
1. DTO里字段聲明成可空字段
給DTO增加可空兼容轉換器@JsonSerializable( converters: primitiveNullableConvertors),
具體字段的應用由業務層來處理兼容, 這樣不至于整個頁面出問題,個別字段的問題,交由service層/repo層來校驗參數,
eg.如在版本迭代中 枚舉的類型可能增加,但是之前版本代碼是沒有的,那么非空類型就出問題了
實在要堅持后端一定不會變,其他頁面傳進來的參數也不會變, 那么就選擇primitiveStrictConvertors或者decodeOrException 會嘗試解析之后再報錯出來!
規范
結合convertor和 fromJson,toJson Api 注入到DTO身上,不應該去手寫解析 手寫/或者手改.g.dart 合并代碼和以及兼容性都有些問題
枚舉的兼容
枚舉默認按名字,如按其他值解析,有如下三種方式 1. 在枚舉值上添加@JsonValue(value)
-
自定義jsonDecoder 這樣枚舉在其他DTO聲明的地方都可以快速適配,
-
在其他DTO 聲明枚舉的字段上增加注解 @JsonKey(fromJson: StatusConverter.fromJson, toJson: StatusConverter.toJson)
不要在其他DTO解析的地方(其他DTO有聲明這個枚舉類型字段) 來手寫if判斷!
/// 日歷賬號類型
enum CalDavTypeEnum {google("google"),iCloud("iCloud"),calDAV("CalDAV");final String value;const CalDavTypeEnum(this.value);
}/// 枚舉值和 JSON 數據的映射關系
Map<String, CalDavTypeEnum> _stringToEnum =CalDavTypeEnum.values.associateBy((e) => e.value);
Map<CalDavTypeEnum, String> _enumToString =CalDavTypeEnum.values.associate((e) => MapEntry(e, e.value));class CalDavTypeEnumDecoder {static CalDavTypeEnum decode(dynamic json) {return _stringToEnum["$json"] ??(throw ArgumentError('Unknown enum value: $json'));}static CalDavTypeEnum? decodeOrNull(dynamic json) {return _stringToEnum["$json"];}
}class CalDavTypeEnumEncoder {static String encode(CalDavTypeEnum? myEnum) {return _enumToString[myEnum] ??(throw ArgumentError('Unknown enum: $myEnum'));}static String? encodeOrNull(CalDavTypeEnum? myEnum) {return _enumToString[myEnum];}
}///解析轉換器
class CalDavTypeEnumJsonConverterextends JsonConverter<CalDavTypeEnum, dynamic> {const CalDavTypeEnumJsonConverter();@overrideCalDavTypeEnum fromJson(json) {return CalDavTypeEnumDecoder.decode(json);}@overridetoJson(CalDavTypeEnum object) {return CalDavTypeEnumEncoder.encode(object);}
}///解析轉換器 可空
class CalDavTypeEnumNullableJsonConverterextends JsonConverter<CalDavTypeEnum?, dynamic> {const CalDavTypeEnumNullableJsonConverter();@overrideCalDavTypeEnum? fromJson(json) {return CalDavTypeEnumDecoder.decodeOrNull(json);}@overridetoJson(CalDavTypeEnum? object) {return CalDavTypeEnumEncoder.encodeOrNull(object);}
}
json_annotation使用指引
?
-
模型增加 @JsonSerializable注解
-
在模型聲明文件頭部增加 part 'xxx.g.dart'; 其中xxx 一般是模型名字,當然也可以是其他名字
-
字段如有必要增加@JsonKey
-
在命令行 cd 到模型對應的目錄
-
在命令行執行 flutter pub run build_runner build
-
那么就能看到生成 xxx.g.dart文件的生成,將這個文件添加到git
-
然后再模型里面聲明方法引用
part 'account_xxx.g.dart';@JsonSerializable()
class AccountInfo {int? id;//聲明反序列化方法factory AccountInfo.fromJson(Map<String, dynamic> json) =>_$AccountInfoFromJson(json);///聲明序列化方法Map<String, dynamic> toJson() => _$AccountInfoToJson(this);
}