iT邦幫忙

2024 iThome 鐵人賽

DAY 15
1
Mobile Development

從零開始以Flutter打造跨平台聊天APP系列 第 15

Day-15 在 Flutter 中自動生成 JSON 序列化程式碼並撰寫單元測試

  • 分享至 

  • xImage
  •  

Generated from Stable Diffusion 3 Medium

JSON (JavaScript Object Notation) 是一種輕量級的資料交換格式,使用簡單的文字來表示物件和陣列。JSON 被設計得易於人類閱讀和編寫,同時也易於機器解析和生成。常用於在伺服器和客戶端之間傳遞資料。

由於 JSON 是由 Javascript 的物件所設計,因此在 dart 中,無法直接使用,要使 JSON 轉成 dart 的物件需要再下一些功夫!在 Day-12 在 Flutter 中使用原生方法及第三方套件實現 http 連線 中,我們有提到怎麼使用 http 與伺服器連線,也有使用 dart:convert 中的 jsonDecode 將 JSON 資料轉為 Map。雖然看似方便,但如果我們希望直接將整個 JSON 格式,轉為一個 Dart 物件時,或著,Dart 物件轉為 JSON 格式時,每次都需要 encode, decode,屬實有點麻煩。此時最直覺的方式,就是將 encode 和 decode 轉為 Dart 物件中的函式。

以下程式 user.dart

class User {
  final String name;
  final int age;

  User(this.name, this.age);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'] as String,
        age = json['age'] as int;

  Map<String, dynamic> toJson() => {
        'name': name,
        'age': age,
      };
}

參考官方文件:https://docs.flutter.dev/data-and-backend/serialization/json

這種將 Dart 物件轉為 JSON 的方式稱為 Serialization (序列化);而將 JSON 轉為 Dart 物件則稱為 Deserialization (反序列化)。這個過程極其枯燥,可以由程式來完成!

本次範例程式碼:https://github.com/ksw2000/ironman-2024/tree/master/flutter-practice/json_serialization

本次教學會提及如何使用 json_serializable 來自動生成「序列化」的程式碼。並使用 flutter 中的單元測試進行測試

使用 json_serializable 自動生成序列化程式碼

首先,我們先安裝 json_serializable 的套件 dev 會將套件加入 dev_dependencies ,此邏輯與 npm 類似,這些套件只有在開發時會使用到,比如用來生成程式碼,而在發佈時並不會使用這些相依套件。

flutter pub add json_annotation dev:build_runner dev:json_serializable

安裝完成後,我們可以將原本的 User 物件改為 User2,並存成 user2.dart

import 'package:json_annotation/json_annotation.dart';

/// 允許 `User2` 類別訪問在生成文件中的 private 成員。
/// *.g.dart,其中星號 * 表示源文件。
part 'user2.g.dart';

/// @ annotation 用於告知程式碼生成器這個類別需要生成 JSON 序列化邏輯。
@JsonSerializable()
class User2 {
  final String name;
  final int age;

  User2(this.name, this.age);

  /// 配合生成的程示碼必需使用 factory 構造函數,用於從 map 中創建新的實例。
  /// 將 map 傳遞給生成的 `_$User2FromJson()` 構造函數。
  /// 構造函數的名稱與原本的 class 相同,比如 User2。
  factory User2.fromJson(Map<String, dynamic> json) => _$User2FromJson(json);
    
  /// `toJson` 是一個序列化 JSON 常用的慣例名稱。
  /// 實現時僅需調用生成的 private 方法 `_$User2ToJson`。
  Map<String, dynamic> toJson() => _$User2ToJson(this);
}

接著我們可以使用 build_runner 生成程式碼

dart run build_runner build --delete-conflicting-outputs

以上指令只能一次性生成,如果我們希望 dart 能一邊觀察我們的 code 一邊生成,可以改用以下指令:

dart run build_runner watch --delete-conflicting-outputs

生成後會產生 user2.g.dart,如下:

.
└── json_serialization (project name)
    └── lib/
        ├── main.dart
        ├── user.dart
        ├── user2.dart
        └── user2.g.dart   (生成程式碼)

透過 part 關鍵字,user2.g.dart 的程式碼編譯時會加入到 user2.dart 中,因此 user2.dart 可以調用 user2.g.dart 中的 private 物件。

撰寫測試檔

我們可以使用 flutter 中的單元測試來對 User2 進行測試。

.
└── json_serialization/
    └── test/
        ├── user2_test.dart   (自己新增)
        └── widget_test.dart  (預設測試檔)

首先我們可以針對反序列化做測試

test('test fromJSON', () {
  const json = '{"name": "日野下花帆", "age": 17}';
  final Map<String, dynamic> jsonMap = jsonDecode(json);
  final user = User2.fromJson(jsonMap);

  expect(user.name, '日野下花帆');
  expect(user.age, 17);
});

並針對序列化做測試

test('test toJson', () {
  final user = User2('藤島慈', 18);
  final jsonMap = user.toJson();
  expect(jsonMap['name'], '藤島慈');
  expect(jsonMap['age'], 18);
});

最後,我們可以使用 flutter test 來運行測試檔

$ flutter test
00:05 +3: All tests passed!

後記:鐵人賽第15天過完一半了,太累了,希望網友們可以幫忙點個愛心


上一篇
Day-14 在 Flutter 中使用 Websocket 及 StreamBuilder
下一篇
Day-16 在 Flutter 中使用推播通知 Firebase Cloud Messaging
系列文
從零開始以Flutter打造跨平台聊天APP25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言