美文网首页
Flutter 数据解析 —— 插件FlutterJsonBea

Flutter 数据解析 —— 插件FlutterJsonBea

作者: 大成小栈 | 来源:发表于2023-07-23 11:00 被阅读0次

    Flutter 使用的 Dart 语言没有反射,无法像 Java 一样通过反射直接将 Json 数据映射为对应的对象实体类对象。官方解决方案是将 Json 数据转换为字典,然后从字典中进行取数使用。但直接从字典中取数很不方便,写代码时没有自动提示很不友好,而且可能在写的时候写错字段名。

    1. FlutterJsonBeanFactory

    另一种方式:将 Json 转换为字典后再映射到对象实体字段里,这样使用时就可以直接使用对应实体类对象。
    FlutterJsonBean就是以这种方式解析,并自动生成映射代码。

    使用AndroidStudio开发,在Setting->Tools插件市场中搜索FlutterJsonBeanFactory,即可安转。
    并且可以在->FlutterJsonBeanFactory里边自定义实体类的后缀,默认是entity。

    2. 创建实体类

    创建一个存放Model的目录,在其上点击右键,再选择看到的JsonToDartBeanAction:

    JsonToDartBeanAction

    Class Name是实体名字,会默认加上entity
    JSON TextJson文本
    null-able勾选后所有属性都是可空的?,不勾选都会加上late,延迟初始化

    事先复制要解析的json到粘贴板,并粘贴至下面的 “Json Text” 中:

    创建实体类

    执行Make后生成代码目录如下:

    • models项目自建,存放实体
    • generated/json是插件生成目录
    • xx_entity.g.daet是实体类生成的辅助类方法
    • base是存放基础公共代码
    image

    2.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);
      }
    }
    

    其中已生成了fromJson的工厂方法和toJson方法,分别调用了xx_entity.g.dart中的$xxEntityFromJson方法和$xxEntityToJson方法。toSring方法会把对象转json字符串显示。

    注:若是修改了实体类xx_entity.dart,鼠标悬停在gernerated目录上,执行Alt+J快捷键,就会自动生成新的映射代码,并且去除多余的。

    2.2 xx_entity.g.dart

    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;
    }
    

    实体类对应的辅助方法文件,g.dart是文件的后缀,存放在generated/json目录下。
    主要包含$xxEntityFromJson$xxEntityToJson方法,$+实体类名为前缀。

    $xxFromJson 将 Json 数据的对应字段取出来然后赋值给实体类的对应字段。Json 数据转换为实体字段使用了 jsonConvert.convert 其定义在 json_convert_content.dart 中。
    $xxToJson将实体数据转换为 Map 字典。

    2.3 json_convert_content.dart

    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)  {...}
    }
    

    json_convert_content.dartJsonConvert类, 用于统一进行 Json 与实体类的转换。

    convert
      T? convert<T>(dynamic value) {
        if (value == null) {
          return null;
        }
        return asT<T>(value);
      }
    

    将json数据转换为实体对象。首先判断了传入的数据是否为null,为null则直接返回null, 不为空则调用asT方法。在生成的.g.dart$xxEntityFromJson方法中非 List 类型字段基本都是调用 convert 方法进行转换。

    convertList
      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>[];
        }
      }
    

    将json数据转换为实体对象List。首先也是判断了传入的数据是否为null ,为null则直接返回null , 不为空则遍历value使用map调用asT方法进行转换,最终还是调用的asT方法。在转换上加了try-catch,如果报错则返回空的List

    converListNotNull
      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>[];
        }
      }
    

    convertList的区别是参数不一样,convertList参数传入的是List<dynamic>convertListNotNull传入的直接是dynamic。其次最大的区别是调用asT方法时convertListNotNull在 asT 后面加了一个 !,表示不为空。
    当在实体类里定义字段为List类型时,会根据是否为List中元素的非空类型而选择生成 convertListconvertListNotNull来进行转换,非空采用convertListNotNull,可空采用convertList

    • List<String?>? foodList1;采用convertList
    • List<String>? foodList2;采用convertListNotNull
    • late List<String?> foodList3;采用convertList
    • late List<String> foodList4;采用convertListNotNull
    as<T>
      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;
        }
      }
    

    首先判断传入的数据类型是否为要转换的数据类型,如果是的话就直接返回传入参数,即如果要将传入数据转换为User,但是传入参数本身就是User类型,那就直接返回。

    然后通过 T.String()获取泛型类型的名称,再与StringintdoubleDateTimebool这些基础数据类型进行比较,如果是这些类型则调用这些类型的转换方法进行转换。

    最后,如果不是基础类型则去_convertFuncMap寻找其它的实体类型,并调用其value,即fromJson方法,类似递归的去解析。

    fromJsonAsT
      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);
        }
      }
    

    判断传入Json数据是否为null,为null则直接返回null。然后判断Json数据是否为List,是 List则调用_getListChildType否则通过全局变量jsonConvert调用asT

    _getListChildType
      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;
      }
    

    <UserEntity>[] is M直接创建对应实体类的空 List 判断是否为泛型类型,如果类型相同,则通过map调用对应实体类的 fromJson方法进行转换。

    3. json_field.dart

    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});
    }
    

    JsonSerializable类注解,二次生成代码时插件查找该注解的类进行生成。
    JSONField字段注解,用于自定义字段映射和配置是否序列化和反序列化字段。

    4. 使用流程

    单实体解析

    直接调用实体类对应的fromJson方法即可将 Json 数据解析为实体对象。

    UserEntity? user;  
    String userData = """
         {
            "id":"1",
            "name":"qi",
            "age":22
         }
        """;
    
    user = UserEntity.fromJson(jsonDecode(userData));
    
    

    调用生成的JsonConvert去解析,使用convertasTfromJsonAsT都能得到结果

    user = jsonConvert.convert<UserEntity>(jsonDecode(userData));
    
    user = jsonConvert.asT<UserEntity>(jsonDecode(userData));
    
    user = JsonConvert.fromJsonAsT<UserEntity>(jsonDecode(userData));
    
    

    List解析

    解析 Json List 数据则需要调用 JsonConvert 的对应方法进行解析,除了使用上面的 convertasTfromJsonAsT 外,还可以使用 convertListconvertListNotNull

    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));
    

    convertListconvertListNotNullconvertasTfromJsonAsT 的区别在于前者的泛型为 List Item元素的泛型类型,后者则直接为对应 List 的类型。如上面 convertListconvertListNotNull的泛型直接为UserEntity , 而 convertasTfromJsonAsT 的泛型为List<UserEntity>

    JSONField的使用

    • 自定义字段名

    处理Json数据字段和实体属性字段不一致的问题,如后台返回Json命名不规范这种情况。可以用JSONField
    自定义字段映射。如后台返回 AGE就可以如下使用,映射成age。加完之后照样需要执行Alt+J

      @JSONField(name: "AGE")
      String? age;
    
    • 忽略字段

    JSONField 还有两个字段 serializedeserialize 用于序列化和反序列化时忽略某个字段。

    5. 解析流程优化

    服务端返回的数据一般是经过了一层包装的,如下:

    {
      "code": 200,
      "message": "success",
      "data":{
        "id": "1",
        "name": "qi1",
        "age": 18
      }
    }
    

    而用上面插件会生成如下代码:

    @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);
      }
    }
    

    如果像上面👆这样,每个接口数据实体都要有一个ResponseEntity,使用起来不便于统一封装。

    所以我们可以把ApiResponseData换成 dynamic,文件底部的ApiResponseData信息也全部删除,再执行Alt+J,这样就会自动清理掉整理json_convert_content.dartapi_response_entity.g.dart中的ApiResponseData痕迹。再把dynamic替换成T,并且去除顶部的@JsonSerializable(),避免下次执行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目录,因为那个目录会自动删除无用的文件。可以和api_reponse_entity.dart单独存放在一个文件夹当中。

    优化后使用

    第一次发现,reponse的data是null。因为新的插件在 asT方法没有去调用fromJsonAsT,这个需要我们自加上,否则会失败。

    if (_convertFuncMap.containsKey(type)) {
      return _convertFuncMap[type]!(value) as T;
    } else {
      return fromJsonAsT<T>(value);
      // throw UnimplementedError('$type unimplemented');
    }
    
      //单实体
      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
            }
          ]
        }
        """;
    
    //基础数据类型
      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();
      }
    

    参考链接:
    https://www.jianshu.com/p/435ca449da41
    https://juejin.cn/post/7043721908801503269
    https://github.com/DachengWang/flutter_app_core

    相关文章

      网友评论

          本文标题:Flutter 数据解析 —— 插件FlutterJsonBea

          本文链接:https://www.haomeiwen.com/subject/rrefpdtx.html