做自(zì)由與創造的先行者

Flutter JSON和(hé)序列化

Flutter開(kāi)發手冊

種JSON序列化方法适合我?

本文(wén)介紹了(le)使用(yòng)JSON的兩個常規策略:

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

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

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

小(xiǎo)項目手動序列化

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

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

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

在大(dà)中型項目中使用(yòng)代碼生成

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

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

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

Flutter中是否有GSON / Jackson / Moshi?

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

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

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

dartson呢(ne)?

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

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

使用(yòng) dart:convert手動序列化JSON

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

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

{

"name": "John Smith",

"email": "john@example.com"

}

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

内連序列化JSON

通過查看(kàn)dart:轉換JSON文(wén)檔,我們發現(xiàn)可以通過調用(yòng)JSON.decode方法來(lái)解碼JSON ,使用(yòng)JSON字符串作(zuò)爲參數。

Map user = JSON.decode(json);

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

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

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

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

在模型類中序列化JSON

我們可以通過引入一個簡單的模型類(model class)來(lái)解決前面提到(dào)的問題,我們稱之爲User。在User類内部,我們有:

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

一個toJson 方法, 将 User 實例轉化爲一個map.

這(zhè)樣,調用(yòng)代碼現(xiàn)在可以具有類型安全、自(zì)動補全字段(name和(hé)email)以及編譯時(shí)異常。如果我們将拼寫錯誤或字段視(shì)爲int類型而不是String, 那麽我們的應用(yòng)程序就不會(huì)通過編譯,而不是在運行時(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,

};

}

現(xiàn)在,序列化邏輯移到(dào)了(le)模型本身内部。采用(yòng)這(zhè)種新方法,我們可以非常容易地反序列化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}.');

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

String json = JSON.encode(user);

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

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

如果有什(shén)麽能(néng)爲我們自(zì)動處理(lǐ)JSON序列化,那将會(huì)非常好(hǎo)。幸運的是,有!

使用(yòng)代碼生成庫序列化JSON

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

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

在項目中設置json_serializable

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

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

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

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

以json_serializable的方式創建model類

讓我們看(kàn)看(kàn)如何将我們的User類轉換爲一個json_serializable。爲了(le)簡單起見,我們使用(yòng)前面示例中的簡化JSON model。

user.dart

import 'package:json_annotation/json_annotation.dart';

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

part 'user.g.dart';

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

@JsonSerializable()

class User{

User(this.name, this.email);

String name;

String email;

//不同的類使用(yòng)不同的mixin即可

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

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

}

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

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

/// Tell json_serializable that "registration_date_millis" should be

/// mapped to this property.

@JsonKey(name: 'registration_date_millis')

final int registrationDateMillis;

運行代碼生成程序

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

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

yet.

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

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

一次性生成

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

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

持續生成

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

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

使用(yòng)json_serializable模型

要通過json_serializable方式反序列化JSON字符串,我們不需要對(duì)先前的代碼進行任何更改。

Map userMap = JSON.decode(json);

var user = new User.fromJson(userMap);

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

String json = JSON.encode(user);

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

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