Flutter如何高效的JSON轉Model

Inlight發表於2020-01-06

背景

由於Flutter禁用執行時反射,所以在Flutter中是沒有GSONJackson這類解析JSON的庫。官方解釋執行時反射會干擾Dart_tree shaking_。使用_tree shaking_我們可以在發版時去除未使用的程式碼。這可以顯著優化應用程式的大小。 由於反射會預設使用所有程式碼,因此_tree shaking_會很難工作。這些工具無法知道哪些widget在執行時未被使用,因此冗餘程式碼很難剝離。使用反射時,應用尺寸無法輕鬆的進行優化。

手動序列化JSON

使用Flutter內建的dart:convert庫做基本的JSON序列化很簡單:

Map<String, dynamic> person = JSON.decode(json);
print('${person['name']}');
print('${person['age']');
複製程式碼

JSON.decode()返回一個Map<String, dynamic>,這意味著我們直到執行時才知道值的型別。這種方法,我們失去了靜態型別語言特性,程式碼非常容易出錯。不推薦。

當然我們也可以手動在模型類中序列化JSON

class Person {
  final String name;
  final String age;

  User(this.name, this.age);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        age = json['age'];

  Map<String, dynamic> toJson() =>
    {
      'name': name,
      'age': age,
    };
}
複製程式碼
Map personMap = JSON.decode(json);
var person = new Person.fromJson(personMap);
print('${person.name}');
print('${person.age}');
複製程式碼

這樣我們呼叫程式碼可以具有型別安全、自動補全欄位以及編譯時異常等靜態型別語言特性。如果拼寫或者型別錯誤就不會通過編譯,而不是在執行時崩潰。

在專案實戰中JSON物件很少會這麼簡單,各種ListMap巢狀的JSON也是很常見的。如何安全且高效的JSONModel才是我們想要的。

使用 json_serializable

json_serializable是一個自動化的原始碼生成器,可以為我們生成JSON序列化模板。在pubspec.yaml中新增依賴並執行flutter pub get

dependencies:
  json_annotation: ^3.0.0

dev_dependencies:
  build_runner: ^1.0.0
  json_serializable: 3.2.0
複製程式碼

這裡沒有使用最新版本因為我們Flutter版本為v1.9.1+hotfix.6Dart版本比較低。

  1. 生成模型類

生成模型類我們使用一位大佬寫的json2dart工具。

假設mock了一個JSON

[
    {
      "name": "小龍女",
      "age": "18",
      "tele": "13888888888"
    },
    {
      "name": "楊過",
      "age": "18",
      "tele": "13666666666"
    },
    {
      "name": "尹志平",
      "age": "18",
      "tele": "13333333333"
    }
]
複製程式碼

工具使用很簡單直接貼上生成對應的類名稱

Flutter如何高效的JSON轉Model
此時我們將生成的程式碼copy出來粘在我們建立的模型類中。

import 'package:json_annotation/json_annotation.dart'; 
  
part 'person_model.g.dart';

List<PersonModel> getPersonModelList(List<dynamic> list){
    List<PersonModel> result = [];
    list.forEach((item){
      result.add(PersonModel.fromJson(item));
    });
    return result;
  }
@JsonSerializable()
  class PersonModel extends Object {

  @JsonKey(name: 'name')
  String name;

  @JsonKey(name: 'age')
  String age;

  @JsonKey(name: 'tele')
  String tele;

  PersonModel(this.name,this.age,this.tele,);

  factory PersonModel.fromJson(Map<String, dynamic> srcJson) => _$PersonModelFromJson(srcJson);

  Map<String, dynamic> toJson() => _$PersonModelToJson(this);

}
複製程式碼

這裡需要注意這個工具可能有少欄位的情況,如果是ListJSON只會取其中的第一個(小龍女)來生成模型的欄位。也就是如果後面的Item(楊過,尹志平)欄位數量大於第一個也會按照第一個的欄位來生成。

  1. 執行程式碼生成程式

上面的模型類生成之後會先報錯,因為模型類的生成程式碼還不存在,所以我們需要執行程式碼生成器來為我們生成序列化模板。

  • 一次性生成flutter packages pub run build_runner build
  • 持續生成flutter packages pub run build_runner watch

這裡選擇哪種方式取決於你的改動頻率,推薦使用watch的方式。

  1. 使用
Map personList = JSON.decode(json);
var list = getPersonModelList(personList);
複製程式碼

json_serializable這種方式,我們可以輕鬆的生成一個模型類。通過原始碼生成器建立一個g.dart的檔案,它具有所有必需的序列化邏輯。

使用工具網站

app.quicktype.io是一個將JSON轉換成模型類的工具網站,目前來看支援大部分常用語言,並且靈活的可選項也非常多:

Flutter如何高效的JSON轉Model

這裡我們還是用上面的JSON做一下嘗試:

Flutter如何高效的JSON轉Model

生成的模型類是使用了Flutter內建的dart:convert做序列化。

// To parse this JSON data, do
//
//     final personModel = personModelFromJson(jsonString);

import 'dart:convert';

List<PersonModel> personModelFromJson(String str) => List<PersonModel>.from(json.decode(str).map((x) => PersonModel.fromJson(x)));

String personModelToJson(List<PersonModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class PersonModel {
    String name;
    String age;
    String tele;

    PersonModel({
        this.name,
        this.age,
        this.tele,
    });

    factory PersonModel.fromJson(Map<String, dynamic> json) => PersonModel(
        name: json["name"],
        age: json["age"],
        tele: json["tele"],
    );

    Map<String, dynamic> toJson() => {
        "name": name,
        "age": age,
        "tele": tele,
    };
}
複製程式碼

可以看到這個模型類正是我們需要的,使用方式也在上面註釋的很清楚,目前來講這種方式操作起來會比使用json_serializable操作起來更簡便一些。

總結

  • 手動序列化JSON:比較麻煩,效率低,但新手還是多做嘗試和了解比較好。
  • json_serializable:效率高,watch很好用。
  • 工具網站:效率高,更多功能可選。

總體推薦使用後兩種,可以大大提升開發效率,不用埋頭去搞一些重複的序列化工作。

相關文章