Flutter 數(shù)據(jù)解析 —— 插件FlutterJsonBeanFactory

Flutter 使用的 Dart 語言沒有反射,無法像 Java 一樣通過反射直接將 Json 數(shù)據(jù)映射為對應(yīng)的對象實體類對象。官方解決方案是將 Json 數(shù)據(jù)轉(zhuǎn)換為字典,然后從字典中進行取數(shù)使用。但直接從字典中取數(shù)很不方便,寫代碼時沒有自動提示很不友好,而且可能在寫的時候?qū)戝e字段名。

1. FlutterJsonBeanFactory

另一種方式:將 Json 轉(zhuǎn)換為字典后再映射到對象實體字段里,這樣使用時就可以直接使用對應(yīng)實體類對象。
FlutterJsonBean就是以這種方式解析,并自動生成映射代碼。

使用AndroidStudio開發(fā),在Setting->Tools插件市場中搜索FlutterJsonBeanFactory,即可安轉(zhuǎn)。
并且可以在->FlutterJsonBeanFactory里邊自定義實體類的后綴,默認是entity。

2. 創(chuàng)建實體類

創(chuàng)建一個存放Model的目錄,在其上點擊右鍵,再選擇看到的JsonToDartBeanAction:

JsonToDartBeanAction

Class Name是實體名字,會默認加上entity
JSON TextJson文本
null-able勾選后所有屬性都是可空的?,不勾選都會加上late,延遲初始化

事先復(fù)制要解析的json到粘貼板,并粘貼至下面的 “Json Text” 中:

創(chuàng)建實體類

執(zhí)行Make后生成代碼目錄如下:

  • models項目自建,存放實體
  • generated/json是插件生成目錄
  • xx_entity.g.daet是實體類生成的輔助類方法
  • base是存放基礎(chǔ)公共代碼
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方法,分別調(diào)用了xx_entity.g.dart中的$xxEntityFromJson方法和$xxEntityToJson方法。toSring方法會把對象轉(zhuǎn)json字符串顯示。

注:若是修改了實體類xx_entity.dart,鼠標(biāo)懸停在gernerated目錄上,執(zhí)行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;
}

實體類對應(yīng)的輔助方法文件,g.dart是文件的后綴,存放在generated/json目錄下。
主要包含$xxEntityFromJson$xxEntityToJson方法,$+實體類名為前綴。

$xxFromJson 將 Json 數(shù)據(jù)的對應(yīng)字段取出來然后賦值給實體類的對應(yīng)字段。Json 數(shù)據(jù)轉(zhuǎn)換為實體字段使用了 jsonConvert.convert 其定義在 json_convert_content.dart 中。
$xxToJson將實體數(shù)據(jù)轉(zhuǎn)換為 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類, 用于統(tǒng)一進行 Json 與實體類的轉(zhuǎn)換。

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

將json數(shù)據(jù)轉(zhuǎn)換為實體對象。首先判斷了傳入的數(shù)據(jù)是否為null,為null則直接返回null, 不為空則調(diào)用asT方法。在生成的.g.dart$xxEntityFromJson方法中非 List 類型字段基本都是調(diào)用 convert 方法進行轉(zhuǎn)換。

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數(shù)據(jù)轉(zhuǎn)換為實體對象List。首先也是判斷了傳入的數(shù)據(jù)是否為null ,為null則直接返回null , 不為空則遍歷value使用map調(diào)用asT方法進行轉(zhuǎn)換,最終還是調(diào)用的asT方法。在轉(zhuǎn)換上加了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的區(qū)別是參數(shù)不一樣,convertList參數(shù)傳入的是List<dynamic>convertListNotNull傳入的直接是dynamic。其次最大的區(qū)別是調(diào)用asT方法時convertListNotNull在 asT 后面加了一個 !,表示不為空。
當(dāng)在實體類里定義字段為List類型時,會根據(jù)是否為List中元素的非空類型而選擇生成 convertListconvertListNotNull來進行轉(zhuǎn)換,非空采用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;
    }
  }

首先判斷傳入的數(shù)據(jù)類型是否為要轉(zhuǎn)換的數(shù)據(jù)類型,如果是的話就直接返回傳入?yún)?shù),即如果要將傳入數(shù)據(jù)轉(zhuǎn)換為User,但是傳入?yún)?shù)本身就是User類型,那就直接返回。

然后通過 T.String()獲取泛型類型的名稱,再與Stringint、double、DateTime、bool這些基礎(chǔ)數(shù)據(jù)類型進行比較,如果是這些類型則調(diào)用這些類型的轉(zhuǎn)換方法進行轉(zhuǎn)換。

最后,如果不是基礎(chǔ)類型則去_convertFuncMap尋找其它的實體類型,并調(diào)用其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數(shù)據(jù)是否為null,為null則直接返回null。然后判斷Json數(shù)據(jù)是否為List,是 List則調(diào)用_getListChildType否則通過全局變量jsonConvert調(diào)用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直接創(chuàng)建對應(yīng)實體類的空 List 判斷是否為泛型類型,如果類型相同,則通過map調(diào)用對應(yīng)實體類的 fromJson方法進行轉(zhuǎn)換。

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. 使用流程

單實體解析

直接調(diào)用實體類對應(yīng)的fromJson方法即可將 Json 數(shù)據(jù)解析為實體對象。

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

List解析

解析 Json List 數(shù)據(jù)則需要調(diào)用 JsonConvert 的對應(yīng)方法進行解析,除了使用上面的 convertasT、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));

convertListconvertListNotNullconvert 、asTfromJsonAsT 的區(qū)別在于前者的泛型為 List Item元素的泛型類型,后者則直接為對應(yīng) List 的類型。如上面 convertList、convertListNotNull的泛型直接為UserEntity , 而 convert 、asT、fromJsonAsT 的泛型為List<UserEntity> 。

JSONField的使用

  • 自定義字段名

處理Json數(shù)據(jù)字段和實體屬性字段不一致的問題,如后臺返回Json命名不規(guī)范這種情況??梢杂肑SONField
自定義字段映射。如后臺返回 AGE就可以如下使用,映射成age。加完之后照樣需要執(zhí)行Alt+J

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

JSONField 還有兩個字段 serialize 、deserialize 用于序列化和反序列化時忽略某個字段。

5. 解析流程優(yōu)化

服務(wù)端返回的數(shù)據(jù)一般是經(jīng)過了一層包裝的,如下:

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

如果像上面??這樣,每個接口數(shù)據(jù)實體都要有一個ResponseEntity,使用起來不便于統(tǒng)一封裝。

所以我們可以把ApiResponseData換成 dynamic,文件底部的ApiResponseData信息也全部刪除,再執(zhí)行Alt+J,這樣就會自動清理掉整理json_convert_content.dartapi_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目錄,因為那個目錄會自動刪除無用的文件??梢院?code>api_reponse_entity.dart單獨存放在一個文件夾當(dāng)中。

優(yōu)化后使用

第一次發(fā)現(xiàn),reponse的data是null。因為新的插件在 asT方法沒有去調(diào)用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
        }
      ]
    }
    """;

//基礎(chǔ)數(shù)據(jù)類型
  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();
  }

參考鏈接:
http://www.itdecent.cn/p/435ca449da41
https://juejin.cn/post/7043721908801503269
https://github.com/DachengWang/flutter_app_core

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容