flutter json解析增强

依赖: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提供如下基础中间件,两种兼容模式,一种给全日志

  1. JsonConverter 兼容基本类型(int,num,double,string,bool),异常情况解析成对应类型的默认值,达到同级别js,oc等语言层兼容

  2. nullable_converter 兼容基本类型(int,num,double,string,bool),异常情况解析成null,需要DTO声明字段为可空类型

  3. strict_converter 先兼容解析,解析不了再报错,解决json解析报错,不提示具体内容,导致一个一个去比较DTO里声明的字段,或者打印stack才可以排查具体字段

至于要使用多少类型兼容和什么策略,请自己选,上图只是模版

用法

  1. DTO层增加注解 通过 @JsonSerializable 注解参数converters 注入!

其中 primitiveConvertors :基本类型 解析器 ,安全处理,如遇到失败会转换成默认值 primitiveNullableConvertors:基本类型解析器,安全处理,如遇到失败变成 null ,适合声明可空类型的字段

primitiveStrictConvertors:基本类型 解析器 ,不安全处理,用于提示内容,比原错误始信息增加 内容本身到日志里面,解决原始报错,不提示具体内容,不好分析 DTO 的那个字段

用法代码示例:

Dart 复制代码
@JsonSerializable(
    converters: [...primitiveNullableConvertors, RRuleJsonAdapter()])
class Event{
}
  1. 字段级别增加注解

通过 @JsonKey fromJson 注解增加参数

用法代码示例:

Dart 复制代码
@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

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)

  1. 自定义jsonDecoder 这样枚举在其他DTO声明的地方都可以快速适配,

  2. 在其他DTO 声明枚举的字段上增加注解 @JsonKey(fromJson: StatusConverter.fromJson, toJson: StatusConverter.toJson)

不要在其他DTO解析的地方(其他DTO有声明这个枚举类型字段) 来手写if判断!

Dart 复制代码
/// 日历账号类型
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 CalDavTypeEnumJsonConverter
    extends JsonConverter<CalDavTypeEnum, dynamic> {
  const CalDavTypeEnumJsonConverter();

  @override
  CalDavTypeEnum fromJson(json) {
    return CalDavTypeEnumDecoder.decode(json);
  }

  @override
  toJson(CalDavTypeEnum object) {
    return CalDavTypeEnumEncoder.encode(object);
  }
}

///解析转换器 可空
class CalDavTypeEnumNullableJsonConverter
    extends JsonConverter<CalDavTypeEnum?, dynamic> {
  const CalDavTypeEnumNullableJsonConverter();

  @override
  CalDavTypeEnum? fromJson(json) {
    return CalDavTypeEnumDecoder.decodeOrNull(json);
  }

  @override
  toJson(CalDavTypeEnum? object) {
    return CalDavTypeEnumEncoder.encodeOrNull(object);
  }
}

json_annotation使用指引

  1. 模型增加 @JsonSerializable注解

  2. 在模型声明文件头部增加 part 'xxx.g.dart'; 其中xxx 一般是模型名字,当然也可以是其他名字

  3. 字段如有必要增加@JsonKey

  4. 在命令行 cd 到模型对应的目录

  5. 在命令行执行 flutter pub run build_runner build

  6. 那么就能看到生成 xxx.g.dart文件的生成,将这个文件添加到git

  7. 然后再模型里面声明方法引用

Dart 复制代码
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);
}
相关推荐
律宏阔10 分钟前
Flutter 列表图片内存优化:CachedNetworkImage 与 ClipRRect 的取舍
前端·flutter
律宏阔20 分钟前
Dart 3 Record:解决 Future.wait 异构返回值的类型问题
前端·flutter
伯伯熊勞2 小时前
GetX 巢狀路由教學:用三層 Navigator 做桌面多欄佈局
flutter
liangshanbo12153 小时前
高级前端面试题:package.json 常见字段
前端·json
传奇开心果编程7 小时前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack
m0_738185829 小时前
Flutter 鸿蒙化实战:foundation_fluttify 适配 OpenHarmony,Fluttify 桥接层
flutter·华为·harmonyos·鸿蒙
m0_738185829 小时前
Flutter 鸿蒙化实战:http_proxy 适配 OpenHarmony,HTTP 代理
flutter·http·华为·harmonyos·鸿蒙
梦帮科技9 小时前
【3.0修订版】创世与节点工程:genesis.json 与 node.toml 解剖
数据结构·算法·json·去中心化·区块链·智能合约·信任链
m0_738185829 小时前
Flutter 鸿蒙化实战:headset_connection_event 适配 OpenHarmony,耳机插拔监听
flutter·华为·harmonyos·鸿蒙
weixin_440730501 天前
内置函数、json文本、pickle二进制
开发语言·python·json