在開(kāi)發(fā)中,服務(wù)端通常給我們返回的是JSON數(shù)據(jù)枢劝,我們需要將JSON數(shù)據(jù)轉(zhuǎn)成我們的模型對(duì)象來(lái)使用您旁。
在Flutter中,有幾種JSON轉(zhuǎn)模型的方式蚕脏,我們還是以豆瓣為例侦锯,來(lái)進(jìn)行一個(gè)演練。
一. 豆瓣數(shù)據(jù)
這里我們使用豆瓣的請(qǐng)求地址:https://douban.uieee.com/v2/movie/top250?start=0&count=20
在瀏覽器中請(qǐng)求挣棕,獲取到的數(shù)據(jù)如下:
- 注意:這里我使用了一個(gè)格式化插件:FeHelper亲桥,所以結(jié)構(gòu)看起來(lái)很清晰
這個(gè)數(shù)據(jù)還是比較復(fù)雜的:
- 如果我們希望在Flutter代碼中使用,直接將JSON轉(zhuǎn)成Map來(lái)使用也可以皂甘,但是非常麻煩悼凑,而且類(lèi)型會(huì)不容易確定璧瞬,并且不安全嗤锉。
- 所以對(duì)于面向?qū)ο箝_(kāi)發(fā)的語(yǔ)言墓塌,我們通常都會(huì)將它轉(zhuǎn)成模型對(duì)象,之后使用一個(gè)個(gè)模型對(duì)象访诱。
我們一起來(lái)探究一下韩肝,目前Flutter中比較常見(jiàn)的將JSON轉(zhuǎn)成模型的方式。
二. 手動(dòng)轉(zhuǎn)化
JSON轉(zhuǎn)模型涡相,必然可以通過(guò)手動(dòng)來(lái)進(jìn)行轉(zhuǎn)化:
- 優(yōu)點(diǎn):完全是自己可控的剩蟀,并且需要哪些字段就轉(zhuǎn)化哪些字段,對(duì)于不需要的丙号,忽略即可且预;并且繼承關(guān)系也會(huì)一目了然
- 缺點(diǎn):麻煩,并且容易出錯(cuò);
下面是我之前針對(duì)上面的數(shù)據(jù)截酷,寫(xiě)的JSON轉(zhuǎn)Model的模型類(lèi):
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"] as List<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轉(zhuǎn)Model的方式:
- 一個(gè)自動(dòng)化源代碼生成器來(lái)為你生成 JSON 序列化數(shù)據(jù)模板三热;
- 由于序列化數(shù)據(jù)代碼不再需要手動(dòng)編寫(xiě)或者維護(hù)三幻,你可以將序列化 JSON 數(shù)據(jù)在運(yùn)行時(shí)的異常風(fēng)險(xiǎn)降到最低;
第一步:添加相關(guān)的依賴
依賴分為項(xiàng)目依賴(dependencies)抑堡,開(kāi)發(fā)依賴(dev_dependencies):
- 注意:需要執(zhí)行flutter pub get確保我們的項(xiàng)目中有這些依賴
dependencies:
json_annotation: ^3.0.1
dev_dependencies:
json_serializable: ^3.2.5
build_runner: ^1.8.0
第二步:以json_serializable 的方式創(chuàng)建模型類(lèi)
這里不以豆瓣數(shù)據(jù)為例,以一個(gè)簡(jiǎn)單的Json數(shù)據(jù)作為例子
final jsonInfo = {
"nickname": "coderwhy",
"level": 18,
"courses": ["語(yǔ)文", "數(shù)學(xué)", "英語(yǔ)"],
"register_date": "2222-2-22",
"computer": {
"brand": "MackBook",
"price": 1000
}
};
創(chuàng)建對(duì)應(yīng)的模型(以json_serializable 的方式首妖,創(chuàng)建完成后代碼是報(bào)錯(cuò)的)
- part 'user.g.dart'
這個(gè)是之后json_serializable會(huì)自動(dòng)幫助我們生成的文件 - @JsonSerializable()
告訴json_serializable哪一個(gè)類(lèi)需要進(jìn)行轉(zhuǎn)換 - @JsonKey
當(dāng)映射關(guān)系不一樣時(shí),可以指定映射關(guān)系 - 另外象踊,這里必須有我們的構(gòu)造方法
- 需要有對(duì)應(yīng)的工廠構(gòu)造器
_$UserToJson(this)調(diào)用的該方法目前會(huì)報(bào)錯(cuò)棚壁,需要json_serializable來(lái)生成 - toString方法不是必須的,是待會(huì)兒進(jìn)行測(cè)試的
User類(lèi)的代碼:
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類(lèi)的代碼:
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序列化代碼
在項(xiàng)目終端運(yùn)行下面的指令:
- 該指令是生成一次JSON序列化的代碼
flutter pub run build_runner build
或運(yùn)行下面的指令:
- 會(huì)監(jiān)聽(tīng)文件的改變史隆,重新生成JSON序列化的代碼
flutter pub run build_runner watch
第四步:測(cè)試代碼
final jsonInfo = {
"nickname": "coderwhy",
"level": 18,
"courses": ["語(yǔ)文", "數(shù)學(xué)", "英語(yǔ)"],
"register_date": "2222-2-22",
"computer": {
"brand": "MackBook",
"price": 1000
}
};
final user = User.fromJson(jsonInfo);
print(user);
更多資料逆害,請(qǐng)查看下面的資源:
-
dart:convert
和JsonCodec
文檔 - Pub 中的 json_serializable package
- GitHub 中的 json_serializable 例子
四. 網(wǎng)頁(yè)轉(zhuǎn)換
目前有一些網(wǎng)頁(yè)魄幕,可以直接將JSON轉(zhuǎn)成Model颖杏。
推薦網(wǎng)頁(yè):https://javiercbk.github.io/json_to_dart/
我們這里以網(wǎng)頁(yè)版本為例,非常簡(jiǎn)單:
注意:可能因?yàn)槎拱甑臄?shù)據(jù)過(guò)于復(fù)雜翼抠,所以在生成的時(shí)候發(fā)現(xiàn)少了一個(gè)Directors類(lèi)获讳,這里我重新復(fù)制對(duì)應(yīng)的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 = new List<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 = new List<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() {
final Map<String, dynamic> data = new Map<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() {
final Map<String, dynamic> data = new Map<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() {
final Map<String, dynamic> data = new Map<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() {
final Map<String, dynamic> data = new Map<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() {
final Map<String, dynamic> data = new Map<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() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['small'] = this.small;
data['large'] = this.large;
data['medium'] = this.medium;
return data;
}
}
五. 編輯器插件
目前也有一些AndroidStudio或者VSCode的插件浑此,來(lái)幫助我們直接將JSON生成對(duì)應(yīng)的Model。
- VSCode目前沒(méi)有找到比較好用的插件推薦
- Android Studio推薦FlutterJsonBeanFactory
第一步:安裝插件
第二步:創(chuàng)建模型
右鍵新建文件:
給類(lèi)起一個(gè)名字紊馏,并且將JSON復(fù)制過(guò)去
第三步:使用生成的模型
創(chuàng)建完成后會(huì)生成對(duì)應(yīng)的模型,并且還會(huì)生成一個(gè)文件夾稀火,里面有生成模型過(guò)程的代碼赌朋,這里不再給出,代碼都是類(lèi)似的沛慢。