做(zuò)自(zì)由與創造的先行(xíng)者

Flutter JSON和(hé)序列化

Flutter開(kāi)發手冊

種JSON序列化方法适合我?

本文介紹了(le)使用JSON的兩個常規策略:

手動序列化和(hé)反序列化

通(tōng)過代碼生成自(zì)動序列化和(hé)反序列化

不同的項目具有不同的複雜(zá)度和(hé)場景。對于較小項目,使用代碼生成器可(kě)能(néng)會過度。對于具有多個JSON model的複雜(zá)應用程序,手動序列化可(kě)能(néng)會比較重複,并會很(hěn)容易出錯。

小項目手動序列化

手動JSON序列化是指使使用dart:convert中內(nèi)置的JSON解碼器。它将原始JSON字符串傳遞給JSON.decode() 方法,然後在返回的Map中查找所需的值。 它沒有外(wài)部依賴或其它的設置,對于小項目很(hěn)方便。

當您的項目變大時(shí),手動編寫序列化邏輯可(kě)能(néng)變得難以管理且容易出錯。如(rú)果您在訪問(wèn)未提供的JSON字段時(shí)輸入了(le)一(yī)個錯誤的字段,則您的代碼将會在運行(xíng)時(shí)會引發錯誤。

如(rú)果您的項目中JSON model并不多,并且希望快(kuài)速測試一(yī)下(xià),那(nà)麽手動序列化可(kě)能(néng)會很(hěn)方便。

在大中型項目中使用代碼生成

代碼生成功能(néng)的JSON序列化是指通(tōng)過外(wài)部庫為(wèi)您自(zì)動生成序列化模闆。它需要(yào)一(yī)些初始設置,并運行(xíng)一(yī)個文件觀察器,從(cóng)您的model類生成代碼。 例如(rú),json_serializable和(hé)built_value就是這(zhè)樣的庫。

這(zhè)種方法适用于較大的項目。不需要(yào)手寫,如(rú)果訪問(wèn)JSON字段時(shí)拼寫錯誤,這(zhè)會在編譯時(shí)捕獲的。代碼生成的不利之處在于它涉及到一(yī)些初始設置。另外(wài),生成的源文件可(kě)能(néng)會在項目導航器會顯得混亂。

當您有一(yī)個中型或大型項目時(shí),您可(kě)能(néng)想要(yào)使用代碼生成JSON序列化。

Flutter中是否有GSON / Jackson / Moshi?

簡單的回答(dá)是沒有.

這(zhè)樣的庫需要(yào)使用運行(xíng)時(shí)反射,這(zhè)在Flutter中是禁用的。運行(xíng)時(shí)反射會幹擾Dart的_tree shaking_。使用_tree shaking_,我們可(kě)以在發版時(shí)“去除”未使用的代碼。這(zhè)可(kě)以顯着優化應用程序的大小。

由于反射會默認使用所有代碼,因此_tree shaking_會很(hěn)難工(gōng)作(zuò)。這(zhè)些工(gōng)具無法知道(dào)哪些widget在運行(xíng)時(shí)未被使用,因此冗餘代碼很(hěn)難剝離。使用反射時(shí),應用尺寸無法輕松的進行(xíng)優化。

dartson呢?

dartson 使用了(le)運行(xíng)時(shí)反射 runtime,所以不能(néng)在Flutter中使用它.

雖然我們不能(néng)在Flutter中使用運行(xíng)時(shí)反射,但(dàn)有些庫為(wèi)我們提供了(le)類似易于使用的API,但(dàn)它們是基于代碼生成的。這(zhè)種方法在代碼生成庫部分有更詳細的介紹。

使用 dart:convert手動序列化JSON

Flutter中基本的JSON序列化非常簡單。Flutter有一(yī)個內(nèi)置dart:convert庫,其中包含一(yī)個簡單的JSON編碼器和(hé)解碼器。

以下(xià)是一(yī)個簡單的user model的示例JSON。

{

"name": "John Smith",

"email": "john@example.com"

}

有了(le)dart:convert,我們可(kě)以用兩種方式來序列化這(zhè)個JSON model。我們來看(kàn)看(kàn)這(zhè)兩種方法:

內(nèi)連序列化JSON

通(tōng)過查看(kàn)dart:轉換JSON文檔,我們發現可(kě)以通(tōng)過調用JSON.decode方法來解碼JSON ,使用JSON字符串作(zuò)為(wèi)參數(shù)。

Map user = JSON.decode(json);

print('Howdy, ${user['name']}!');

print('We sent the verification link to ${user['email']}.');

不幸的是,JSON.decode()僅返回一(yī)個Map,這(zhè)意味着我們直到運行(xíng)時(shí)才知道(dào)值的類型。 通(tōng)過這(zhè)種方法,我們失去了(le)大部分靜态類型語言特性:類型安全、自(zì)動補全和(hé)最重要(yào)的編譯時(shí)異常。這(zhè)樣一(yī)來,我們的代碼可(kě)能(néng)會變得非常容易出錯。

例如(rú),當我們訪問(wèn)name或email字段時(shí),我們輸入的很(hěn)快(kuài),導緻字段名打錯了(le)。但(dàn)由于這(zhè)個JSON在map結構中,所以編譯器不知道(dào)這(zhè)個錯誤的字段名(譯者語:所以編譯時(shí)不會報錯)。

在模型類中序列化JSON

我們可(kě)以通(tōng)過引入一(yī)個簡單的模型類(model class)來解決前面提到的問(wèn)題,我們稱之為(wèi)User。在User類內(nèi)部,我們有:

一(yī)個User.fromJson 構造函數(shù), 用于從(cóng)一(yī)個map構造出一(yī)個 User實例 map structure

一(yī)個toJson 方法, 将 User 實例轉化為(wèi)一(yī)個map.

這(zhè)樣,調用代碼現在可(kě)以具有類型安全、自(zì)動補全字段(name和(hé)email)以及編譯時(shí)異常。如(rú)果我們将拼寫錯誤或字段視(shì)為(wèi)int類型而不是String, 那(nà)麽我們的應用程序就不會通(tōng)過編譯,而不是在運行(xíng)時(shí)崩潰。

user.dart

class User {

final String name;

final String email;

User(this.name, this.email);

User.fromJson(Map json)

: name = json['name'],

email = json['email'];

Map toJson() =>

{

'name': name,

'email': email,

};

}

現在,序列化邏輯移到了(le)模型本身內(nèi)部。采用這(zhè)種新方法,我們可(kě)以非常容易地(dì)反序列化user。

Map userMap = JSON.decode(json);

var user = new User.fromJson(userMap);

print('Howdy, ${user.name}!');

print('We sent the verification link to ${user.email}.');

要(yào)序列化一(yī)個user,我們隻是将該User對象傳遞給該JSON.encode方法。我們不需要(yào)手動調用toJson這(zhè)個方法,因為(wèi)JSON.encode已經為(wèi)我們做(zuò)了(le)。

String json = JSON.encode(user);

這(zhè)樣,調用代碼就不用擔心JSON序列化了(le)。但(dàn)是,model類還是必須的。在生産應用程序中,我們希望确保序列化正常工(gōng)作(zuò)。在實踐中,User.fromJson和(hé)User.toJson方法都(dōu)需要(yào)單元測試到位,以驗證正确的行(xíng)為(wèi)。

另外(wài),實際場景中,JSON對象很(hěn)少(shǎo)會這(zhè)麽簡單,嵌套的JSON對象并不罕見(jiàn)。

如(rú)果有什麽能(néng)為(wèi)我們自(zì)動處理JSON序列化,那(nà)将會非常好。幸運的是,有!

使用代碼生成庫序列化JSON

盡管還有其他(tā)庫可(kě)用,但(dàn)在本教程中,我們使用了(le)json_serializable package包。 它是一(yī)個自(zì)動化的源代碼生成器,可(kě)以為(wèi)我們生成JSON序列化模闆。

由于序列化代碼不再由我們手寫和(hé)維護,我們将運行(xíng)時(shí)産生JSON序列化異常的風(fēng)險降至最低(dī)。

在項目中設置json_serializable

要(yào)包含json_serializable到我們的項目中,我們需要(yào)一(yī)個常規和(hé)兩個開(kāi)發依賴項。簡而言之,開(kāi)發依賴項是不包含在我們的應用程序源代碼中的依賴項。

通(tōng)過此鏈接可(kě)以查看(kàn)這(zhè)些所需依賴項的最新版本 。

pubspec.yaml

dependencies:

# Your other regular dependencies here

json_annotation: ^2.0.0

dev_dependencies:

# Your other dev_dependencies here

build_runner: ^1.0.0

json_serializable: ^2.0.0

在您的項目根文件夾中運行(xíng) flutter packages get (或者在編輯器中點擊 “Packages Get”) 以在項目中使用這(zhè)些新的依賴項.

以json_serializable的方式創建model類

讓我們看(kàn)看(kàn)如(rú)何将我們的User類轉換為(wèi)一(yī)個json_serializable。為(wèi)了(le)簡單起見(jiàn),我們使用前面示例中的簡化JSON model。

user.dart

import 'package:json_annotation/json_annotation.dart';

// user.g.dart 将在我們運行(xíng)生成命令後自(zì)動生成

part 'user.g.dart';

///這(zhè)個标注是告訴生成器,這(zhè)個類是需要(yào)生成Model類的

@JsonSerializable()

class User{

User(this.name, this.email);

String name;

String email;

//不同的類使用不同的mixin即可(kě)

factory User.fromJson(Map json) => _$UserFromJson(json);

Map toJson() => _$UserToJson(this);

}

有了(le)這(zhè)個設置,源碼生成器将生成用于序列化name和(hé)email字段的JSON代碼。

如(rú)果需要(yào),自(zì)定義命名策略也很(hěn)容易。例如(rú),如(rú)果我們正在使用的API返回帶有_snake_case_的對象,但(dàn)我們想在我們的模型中使用_lowerCamelCase_, 那(nà)麽我們可(kě)以使用@JsonKey标注:

/// Tell json_serializable that "registration_date_millis" should be

/// mapped to this property.

@JsonKey(name: 'registration_date_millis')

final int registrationDateMillis;

運行(xíng)代碼生成程序

json_serializable第一(yī)次創建類時(shí),您會看(kàn)到與下(xià)圖類似的錯誤。

IDE warning when the generated code for a model class does not exist

yet.

這(zhè)些錯誤是完全正常的,這(zhè)是因為(wèi)model類的生成代碼還不存在。為(wèi)了(le)解決這(zhè)個問(wèn)題,我們必須運行(xíng)代碼生成器來為(wèi)我們生成序列化模闆。

There are two ways of running the code generator. 有兩種運行(xíng)代碼生成器的方法:

一(yī)次性生成

通(tōng)過在我們的項目根目錄下(xià)運行(xíng)flutter packages pub run build_runner build,我們可(kě)以在需要(yào)時(shí)為(wèi)我們的model生成json序列化代碼。 這(zhè)觸發了(le)一(yī)次性構建,它通(tōng)過我們的源文件,挑選相關的并為(wèi)它們生成必要(yào)的序列化代碼。

雖然這(zhè)非常方便,但(dàn)如(rú)果我們不需要(yào)每次在model類中進行(xíng)更改時(shí)都(dōu)要(yào)手動運行(xíng)構建命令的話會更好。

持續生成

使用_watcher_可(kě)以使我們的源代碼生成的過程更加方便。它會監視(shì)我們項目中文件的變化,并在需要(yào)時(shí)自(zì)動構建必要(yào)的文件。我們可(kě)以通(tōng)過flutter packages pub run build_runner watch在項目根目錄下(xià)運行(xíng)來啓動_watcher_。

隻需啓動一(yī)次觀察器,然後并讓它在後台運行(xíng),這(zhè)是安全的。

使用json_serializable模型

要(yào)通(tōng)過json_serializable方式反序列化JSON字符串,我們不需要(yào)對先前的代碼進行(xíng)任何更改。

Map userMap = JSON.decode(json);

var user = new User.fromJson(userMap);

序列化也一(yī)樣。調用API與之前相同。

String json = JSON.encode(user);

有了(le)json_serializable,我們可(kě)以在User類上(shàng)忘記任何手動的JSON序列化 。源代碼生成器創建一(yī)個名為(wèi)user.g.dart的文件,它具有所有必需的序列化邏輯。 現在,我們不必編寫自(zì)動化測試來确保序列化的正常工(gōng)作(zuò) - 這(zhè)個庫會确保序列化工(gōng)作(zuò)正常。

網站建設開(kāi)發|APP設計開(kāi)發|小程序建設開(kāi)發
下(xià)一(yī)篇:Flutter 使用 Flutter IDE
上(shàng)一(yī)篇:Flutter 網絡和(hé)Http