Flutter(九) 如何JSON轉Model

在開發中,服務端通常給我們返回的是JSON數據,我們需要將JSON數據轉成我們的模型對象來使用。

在Flutter中,有幾種JSON轉模型的方式,我們還是以豆瓣爲例,來進行一個演練;

一. 豆瓣數據

這裏我們使用豆瓣的請求地址:

  • https://douban.uieee.com/v2/movie/top250?start=0&count=20

在瀏覽器中請求,獲取到的數據如下:

  • 注意:這裏我使用了一個格式化插件:FeHelper,所以結構看起來很清晰

這個數據還是比較複雜的:

  • 如果我們希望在Flutter代碼中使用,直接將JSON轉成Map來使用也可以,但是非常麻煩,而且類型會不容易確定,並且不安全;
  • 所以對於面向對象開發的語言,我們通常都會將它轉成模型對象,之後使用一個個模型對象;

我們一起來探究一下,目前Flutter中比較常見的將JSON轉成模型的方式。

二. 手動轉化

JSON轉模型,必然可以通過手動來進行轉化:

  • 優點:完全是自己可控的,並且需要哪些字段就轉化哪些字段,對於不需要的,忽略即可;並且繼承關係也會一目瞭然
  • 缺點:麻煩,並且容易出錯;

下面是我之前針對上面的數據,寫的JSON轉Model的模型類:

class Person {
  String name;
  String avatarURL;

  Person.fromMap(Map<String, dynamic> json) {
    this.name = json["name"];
    this.avatarURL = json["avatars"]["medium"];
  }
}

class Actor extends Person {
  Actor.fromMap(Map<String, dynamic> json): super.fromMap(json);
}

class Director extends Person {
  Director.fromMap(Map<String, dynamic> json): super.fromMap(json);
}

int counter = 1;

class MovieItem {
  int rank;
  String imageURL;
  String title;
  String playDate;
  double rating;
  List<String> genres;
  List<Actor> casts;
  Director director;
  String originalTitle;

  MovieItem.fromMap(Map<String, dynamic> json) {
    this.rank = counter++;
    this.imageURL = json["images"]["medium"];
    this.title = json["title"];
    this.playDate = json["year"];
    this.rating = json["rating"]["average"];
    this.genres = json["genres"].cast<String>();
    this.casts = (json["casts"] asList<dynamic>).map((item) {
      return Actor.fromMap(item);
    }).toList();
    this.director = Director.fromMap(json["directors"][0]);
    this.originalTitle = json["original_title"];
  }
}

三. json_serializable

json_serializable是dart官方推薦和提供的JSON轉Model的方式:

  • 一個自動化源代碼生成器來爲你生成 JSON 序列化數據模板;
  • 由於序列化數據代碼不再需要手動編寫或者維護,你可以將序列化 JSON 數據在運行時的異常風險降到最低;

第一步:添加相關的依賴

依賴分爲項目依賴(dependencies),開發依賴(dev_dependencies):

  • 注意:需要執行flutter pub get確保我們的項目中有這些依賴
dependencies:
  json_annotation:^3.0.1

dev_dependencies:
  json_serializable:^3.2.5
  build_runner:^1.8.0

第二步:以json_serializable 的方式創建模型類

這裏不以豆瓣數據爲例,以一個簡單的Json數據作爲例子

final jsonInfo = {
    "nickname": "coderwhy",
    "level": 18,
    "courses": ["語文", "數學", "英語"],
    "register_date": "2222-2-22",
    "computer": {
      "brand": "MackBook",
      "price": 1000
    }
  };

創建對應的模型(以json_serializable 的方式,創建完成後代碼是報錯的)

  • 1.part ‘user.g.dart’
    • 這個是之後json_serializable會自動幫助我們生成的文件
  • 2.JsonSerializable()
    • 註解:告訴json_serializable哪一個類需要進行轉換
  • 3.JsonKey
    • 當映射關係不一樣時,可以指定映射關係
  • 4.另外,這裏必須有我們的構造方法
  • 5.需要有對應的工廠構造器
    • _UserFromJson(json) 和 _UserToJson(this)調用的該方法目前會報錯,需要json_serializable來生成
  • 6.toString方法不是必須的,是待會兒進行測試的

User類的代碼:

import'package:json_annotation/json_annotation.dart';
import'model/computer.dart';

part'user.g.dart';

@JsonSerializable()
class User {
  String name;
  String email;
  @JsonKey(name: "register_date")
  String registerDate;
  List<String> courses;
  Computer computer;

  User(this.name, this.email, this.registerDate, this.courses, this.computer);

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);

  @override
  String toString() {
    return'User{name: $name, email: $email, registerDate: $registerDate, courses: $courses, computer: $computer}';
  }
}

Computer類的代碼:

import'package:json_annotation/json_annotation.dart';

part'computer.g.dart';

@JsonSerializable()
class Computer {
  String brand;
  double price;

  Computer(this.brand, this.price);

  factory Computer.fromJson(Map<String, dynamic> json) => _$ComputerFromJson(json);
  Map<String, dynamic> toJson() => _$ComputerToJson(this);

  @override
  String toString() {
    return'Computer{brand: $brand, price: $price}';
  }
}

第三步:生成JSON序列化代碼

在項目終端運行下面的指令:

  • 該指令是生成一次JSON序列化的代碼
flutter pub run build_runner build

或運行下面的指令:

  • 會監聽文件的改變,重新生成JSON序列化的代碼
flutter pub run build_runner watch

第四步:測試代碼

final jsonInfo = {
  "nickname": "coderwhy",
  "level": 18,
  "courses": ["語文", "數學", "英語"],
  "register_date": "2222-2-22",
  "computer": {
    "brand": "MackBook",
    "price": 1000
  }
};

final user = User.fromJson(jsonInfo);
print(user);

更多資料,請查看下面的資源:

  • dart:convertJsonCodec 文檔
  • Pub 中的 json_serializable package
  • GitHub 中的 json_serializable 例子

四. 網頁轉換

目前有一些網頁,可以直接將JSON轉成Model

  • 網頁推薦:https://javiercbk.github.io/json_to_dart/

我們這裏以網頁版本爲例,非常簡單:

  • 注意:可能因爲豆瓣的數據過於複雜,所以在生成的時候發現少了一個Directors類
  • 這裏我重新複製對應的JSON,再次生成了一下
class MovieItem {
  Rating rating;
  List<String> genres;
  String title;
  List<Casts> casts;
  List<String> durations;
  int collectCount;
  String mainlandPubdate;
  bool hasVideo;
  String originalTitle;
  String subtype;
  List<Directors> directors;
  List<String> pubdates;
  String year;
  Avatars images;
  String alt;
  String id;

  MovieItem(
      {this.rating,
        this.genres,
        this.title,
        this.casts,
        this.durations,
        this.collectCount,
        this.mainlandPubdate,
        this.hasVideo,
        this.originalTitle,
        this.subtype,
        this.directors,
        this.pubdates,
        this.year,
        this.images,
        this.alt,
        this.id});

  MovieItem.fromJson(Map<String, dynamic> json) {
    rating =
    json['rating'] != null ? new Rating.fromJson(json['rating']) : null;
    genres = json['genres'].cast<String>();
    title = json['title'];
    if (json['casts'] != null) {
      casts = newList<Casts>();
      json['casts'].forEach((v) {
        casts.add(new Casts.fromJson(v));
      });
    }
    durations = json['durations'].cast<String>();
    collectCount = json['collect_count'];
    mainlandPubdate = json['mainland_pubdate'];
    hasVideo = json['has_video'];
    originalTitle = json['original_title'];
    subtype = json['subtype'];
    if (json['directors'] != null) {
      directors = newList<Directors>();
      json['directors'].forEach((v) {
        directors.add(new Directors.fromJson(v));
      });
    }
    pubdates = json['pubdates'].cast<String>();
    year = json['year'];
    images =
    json['images'] != null ? new Avatars.fromJson(json['images']) : null;
    alt = json['alt'];
    id = json['id'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    if (this.rating != null) {
      data['rating'] = this.rating.toJson();
    }
    data['genres'] = this.genres;
    data['title'] = this.title;
    if (this.casts != null) {
      data['casts'] = this.casts.map((v) => v.toJson()).toList();
    }
    data['durations'] = this.durations;
    data['collect_count'] = this.collectCount;
    data['mainland_pubdate'] = this.mainlandPubdate;
    data['has_video'] = this.hasVideo;
    data['original_title'] = this.originalTitle;
    data['subtype'] = this.subtype;
    if (this.directors != null) {
      data['directors'] = this.directors.map((v) => v.toJson()).toList();
    }
    data['pubdates'] = this.pubdates;
    data['year'] = this.year;
    if (this.images != null) {
      data['images'] = this.images.toJson();
    }
    data['alt'] = this.alt;
    data['id'] = this.id;
    return data;
  }
}

class Rating {
  int max;
  double average;
  Details details;
  String stars;
  int min;

  Rating({this.max, this.average, this.details, this.stars, this.min});

  Rating.fromJson(Map<String, dynamic> json) {
    max = json['max'];
    average = json['average'];
    details =
    json['details'] != null ? new Details.fromJson(json['details']) : null;
    stars = json['stars'];
    min = json['min'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    data['max'] = this.max;
    data['average'] = this.average;
    if (this.details != null) {
      data['details'] = this.details.toJson();
    }
    data['stars'] = this.stars;
    data['min'] = this.min;
    return data;
  }
}

class Details {
  int i1;
  int i2;
  int i3;
  int i4;
  int i5;

  Details({this.i1, this.i2, this.i3, this.i4, this.i5});

  Details.fromJson(Map<String, dynamic> json) {
    i1 = json['1'];
    i2 = json['2'];
    i3 = json['3'];
    i4 = json['4'];
    i5 = json['5'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    data['1'] = this.i1;
    data['2'] = this.i2;
    data['3'] = this.i3;
    data['4'] = this.i4;
    data['5'] = this.i5;
    return data;
  }
}

class Casts {
  Avatars avatars;
  String nameEn;
  String name;
  String alt;
  String id;

  Casts({this.avatars, this.nameEn, this.name, this.alt, this.id});

  Casts.fromJson(Map<String, dynamic> json) {
    avatars =
    json['avatars'] != null ? new Avatars.fromJson(json['avatars']) : null;
    nameEn = json['name_en'];
    name = json['name'];
    alt = json['alt'];
    id = json['id'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    if (this.avatars != null) {
      data['avatars'] = this.avatars.toJson();
    }
    data['name_en'] = this.nameEn;
    data['name'] = this.name;
    data['alt'] = this.alt;
    data['id'] = this.id;
    return data;
  }
}

class Directors {
  Avatars avatars;
  String nameEn;
  String name;
  String alt;
  String id;

  Directors({this.avatars, this.nameEn, this.name, this.alt, this.id});

  Directors.fromJson(Map<String, dynamic> json) {
    avatars =
    json['avatars'] != null ? new Avatars.fromJson(json['avatars']) : null;
    nameEn = json['name_en'];
    name = json['name'];
    alt = json['alt'];
    id = json['id'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    if (this.avatars != null) {
      data['avatars'] = this.avatars.toJson();
    }
    data['name_en'] = this.nameEn;
    data['name'] = this.name;
    data['alt'] = this.alt;
    data['id'] = this.id;
    return data;
  }
}

class Avatars {
  String small;
  String large;
  String medium;

  Avatars({this.small, this.large, this.medium});

  Avatars.fromJson(Map<String, dynamic> json) {
    small = json['small'];
    large = json['large'];
    medium = json['medium'];
  }

  Map<String, dynamic> toJson() {
    finalMap<String, dynamic> data = newMap<String, dynamic>();
    data['small'] = this.small;
    data['large'] = this.large;
    data['medium'] = this.medium;
    return data;
  }
}

五. 編輯器插件

目前也有一些AndroidStudio或者VSCode的插件,來幫助我們直接將JSON生成對應的Model

  • VSCode目前沒有找到比較好用的插件推薦
  • Android Studio推薦FlutterJsonBeanFactory

第一步:安裝插件
第二步:創建模型

  • 右鍵新建文件:
  • 給類起一個名字,並且將JSON複製過去

第三步:使用生成的模型

創建完成後會生成對應的模型,並且還會生成一個文件夾,裏面有生成模型過程的代碼

  • 這裏不再給出,代碼都是相似的
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章