1. 程式人生 > >[譯]Flutter JSON和序列化

[譯]Flutter JSON和序列化

內置 編碼器 實用程序 技術分享 key pac 發布 們的 -a

[譯]Flutter JSON和序列化

很難想象一個移動應用程序不需要與Web服務器通信或在某些時候容易存儲結構化數據。制作網絡連接的應用程序時,遲早需要消耗一些好的舊JSON。

本指南介紹了如何在Flutter中使用JSON。它涵蓋了在不同場景中使用哪種JSON解決方案,以及原因。

哪種JSON序列化方法適合我?

本文介紹了使用JSON的兩種常規策略:

  • 手動序列化
  • 使用代碼生成進行自動序列化

不同的項目具有不同的復雜性和用例。對於較小的概念驗證項目或快速原型,使用代碼生成器可能過度。對於具有更多復雜性的多個JSON模型的應用程序,手動編碼很快就會變得乏味,重復,並且適用於許多小錯誤。

對較小的項目使用手動序列化

手動JSON解碼是指使用內置的JSON解碼器 dart:convert。它涉及將原始JSON字符串傳遞給json.decode() 方法,然後Map<String, dynamic> 在方法返回時查找所需的值。它沒有外部依賴性或特定的設置過程,它有利於快速驗證概念。

當項目變大時,手動解碼效果不佳。手動編寫解碼邏輯可能變得難以管理且容易出錯。如果在訪問不存在的JSON字段時出現拼寫錯誤,則代碼會在運行時拋出錯誤。

如果您的項目中沒有很多JSON模型,並且希望快速測試概念,那麽手動序列化可能就是您想要的方式。有關手動編碼的示例,請參閱 使用dart:convert手動序列化JSON。

使用代碼生成中大型項目

使用代碼生成的JSON序列化意味著使用外部庫為您生成編碼樣板。進行一些初始設置後,您將運行一個文件監視器,從您的模型類生成代碼。例如, json_serializable和 built_value 就是這些類型的庫。

這種方法適用於較大的項目。不需要手寫的樣板文件,並且在編譯時捕獲訪問JSON字段時的拼寫錯誤。代碼生成的缺點是它需要一些初始設置。此外,生成的源文件可能會在項目導航器中產生視覺混亂。

當您擁有中型或大型項目時,您可能希望使用生成的代碼進行JSON序列化。要查看基於JSON編碼的代碼生成示例,請參閱 使用代碼生成庫序列化JSON。

Flutter中是否有GSON / Jackson / Moshi等價物?

簡單回答是不。

這樣的庫需要使用運行時反射,這在Flutter中被禁用。運行時反射會幹擾樹抖動,Dart已經支持了很長時間。在樹搖動的情況下,您可以從發布版本中“擺脫”未使用的代碼。這顯著優化了應用程序的大小。

由於反射使得默認情況下隱式使用所有代碼,因此使樹難以振動。這些工具無法知道運行時哪些部分未使用,因此冗余代碼很難剝離。使用反射時,應用程序大小無法輕松優化。

** dartson怎麽樣?**

該dartson庫使用運行時反射,這使得它不兼容flutter。

雖然您不能在Flutter中使用運行時反射,但是某些庫為您提供了類似的易用API,而是基於代碼生成。代碼生成庫部分更詳細地介紹了此方法。

使用dart:convert手動序列化JSON

Flutter中的基本JSON編碼非常簡單。Flutter有一個內置 dart:convert庫,包括一個簡單的JSON編碼器和解碼器。

以下是簡單用戶模型的示例JSON。

{
  "name": "John Smith",
  "email": "[email protected]"
}

有了dart:convert,您可以通過兩種方式對此JSON模型進行編碼。

序列化JSON內聯

通過查看dart:轉換JSON文檔,您將看到可以通過調用json.decode方法解碼JSON ,並使用JSON字符串作為方法參數。

Map<String, dynamic> user = json.decode(json);

print(‘Howdy, ${user[‘name‘]}!‘);
print(‘We sent the verification link to ${user[‘email‘]}.‘);

不幸的是,json.decode()只返回a Map<String, dynamic>,這意味著在運行時之前您不知道值的類型。使用這種方法,您將丟失大多數靜態類型語言功能:類型安全性,自動完成以及最重要的編譯時異常。您的代碼將立即變得更容易出錯。

例如,無論何時訪問nameemail字段,都可能會快速引入拼寫錯誤。由於JSON存在於地圖結構中,編譯器不知道的拼寫錯誤。

在模型類中序列化JSON

通過引入User在此示例中調用的普通模型類來對抗前面提到的問題。在User課堂上,你會發現:

  • 一個User.fromJson構造函數,構造一個新的User從地圖結構實例。
  • 一種toJsonUser實例轉換為地圖的方法。

使用這種方法,調用代碼可以具有類型安全性,nameemail字段的自動完成以及編譯時異常。如果您使用拼寫錯誤或將字段視為ints而不是Strings,則應用程序將無法編譯,而不是在運行時崩潰。

user.dart

class User {
  final String name;
  final String email;

  User(this.name, this.email);

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

  Map<String, dynamic> toJson() =>
    {
      ‘name‘: name,
      ‘email‘: email,
    };
}

解碼邏輯的責任現在在模型本身內部移動。使用這種新方法,您可以輕松解碼用戶。

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對象傳遞給json.encode方法。您不需要調用該toJson方法,因為json.encode 已經為您完成了。

String json = json.encode(user);

使用這種方法,調用代碼根本不必擔心JSON序列化。但是,模型類仍然必須。在生產應用程序中,您需要確保序列化正常工作。在實踐中,這些User.fromJsonUser.toJson 方法都需要進行單元測試以驗證正確的行為。

但是,現實場景通常不那麽簡單。您不太可能使用如此小的JSON響應。嵌套的JSON對象也是常用的。

如果有一些東西可以為您處理JSON編碼和解碼,那就太好了。幸運的是,有!

使用代碼生成庫序列化JSON

雖然還有其他庫可用,但本指南使用 json_serializable包,這是一個自動生成的源代碼生成器,可為您生成JSON序列化樣板。

由於序列化代碼不再是手動或手動維護的,因此可以最大限度地降低在運行時出現JSON序列化異常的風險。

在項目中設置json_serializable

要包含json_serializable在項目中,您需要一個常規依賴項和兩個dev依賴項。簡而言之,dev依賴項 是我們的應用程序源代碼中未包含的依賴項 - 它們僅在開發環境中使用。

可以通過遵循 JSON可序列化示例中的pubspec文件來查看這些必需依賴項的最新版本 。

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

flutter packages get在項目根文件夾中運行(或單擊 編輯器中的Packages Get)以在項目中使用這些新的依賴項。

以json_serializable方式創建模型類
以下顯示如何將User類轉換為一個類json_serializable 。為簡單起見,此代碼使用先前示例中的簡化JSON模型。

user.dart

class User {
  final String name;
  final String email;

  User(this.name, this.email);

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

  Map<String, dynamic> toJson() =>
    {
      ‘name‘: name,
      ‘email‘: email,
    };
}

采用這種設置,源代碼生成器用於編碼和將編碼生成代碼nameemail從JSON字段。

如果需要,還可以輕松自定義命名策略。例如,如果API返回帶有snake_case的對象,並且您想在模型中使用 lowerCamelCase,則可以使用@JsonKey帶有name參數的註釋:

/// Tell json_serializable that "registration_date_millis" should be
/// mapped to this property.
@JsonKey(name: ‘registration_date_millis‘)
final int registrationDateMillis;

運行代碼生成實用程序

json_serializable第一次創建類時,您將收到類似於下圖所示的錯誤。

技術分享圖片
當模型類的生成代碼尚不存在時,IDE警告。

這些錯誤完全正常,僅僅是因為模型類的生成代碼尚不存在。要解決此問題,請運行生成序列化樣板的代碼生成器。

有兩種運行代碼生成器的方法。

一次性代碼生成

通過flutter packages pub run build_runner build在項目根目錄中運行,可以在需要時為模型生成JSON序列化代碼。這會觸發一次性構建,該構建遍歷源文件,選擇相關文件,並為它們生成必要的序列化代碼。

雖然這很方便,但如果您不必每次在模型類中進行更改時都必須手動運行構建,那將是很好的。

不斷生成代碼

一個觀察者,使我們的源代碼生成的過程更加方便。它會監視項目文件中的更改,並在需要時自動構建必要的文件。通過flutter packages pub run build_runner watch在項目根目錄中運行來啟動觀察程序 。

啟動觀察者一次並讓它在後臺運行是安全的。

使用json_serializable模型

要以這種json_serializable方式解碼JSON字符串,您實際上沒有對我們以前的代碼進行任何更改。

Map userMap = json.decode(json);
var user = User.fromJson(userMap);

編碼也是如此。調用API與以前相同。

String json = json.encode(user);

有了json_serializable,您可以忘記User該類中的任何手動JSON序列化 。源代碼生成器創建一個名為的文件user.g.dart,該文件具有所有必需的序列化邏輯。您不再需要編寫自動化測試來確保序列化工作 - 現在圖書館有責任確保序列化正常工作。

進一步參考

有關更多信息,請參閱以下資源:

  • JsonCodec文檔
  • Pub中的json_serializable包
  • GitHub中的json_serializable示例

[譯]Flutter JSON和序列化