使用 json_serializable


在〈JSON 與 dart:convert〉中的範例,必須自行實作 User 類別的 fromJsontoJson 的內容,這個動作蠻無趣,只是一些對照的動作,你可以透過套件來自動產生這些內容,例如 json_serializable

json_serializable 透過 json_annotation 標註的資訊,結合 build_runner 來自動產生程式碼,因此必須在 pubspec.yaml 中加入:

dependencies:
  json_annotation: ^3.0.1

dev_dependencies:
  build_runner: ^1.10.0
  json_serializable: ^3.4.1

以〈JSON 與 dart:convert〉中的範例來說,接著你可以定義 user.dart:

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart'; // 這個檔案的程式碼是 User 定義的一部份,它稍後才會建構產生

@JsonSerializable() // 標註這個類別中的程式碼要自動產生
class User {
  String name;
  int age;

  User({this.name, this.age});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

接著執行 flutter packages pub run build_runner build,建構出 user.g.dart,內容會是:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'user.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

User _$UserFromJson(Map<String, dynamic> json) {
  return User(
    name: json['name'] as String,
    age: json['age'] as int,
  );
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'age': instance.age,
    };

接下來使用的方式就跟〈JSON 與 dart:convert〉中的範例相同:

var json = jsonEncode(User(name: 'Justin', age: 40));
var user = User.fromJson(jsonDecode(json));

print(json);
print(user);

在產生的 user.g.dart 中可以看到,預設會使用 User 的欄位名稱來作為 Map 的 key,如果呼叫的 API 使用的 key 與欄位名稱不同,可以使用 @JsonKey 來標註,例如:

import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
  String name;
  int age;

  // 標註 API 的 key 與欄位名稱的對應
  @JsonKey(name: 'address') 
  String email;

  User({this.name, this.age, this.email});

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

如此一來,產生的 user.g.dart 會是:

// GENERATED CODE - DO NOT MODIFY BY HAND

part of 'user.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

User _$UserFromJson(Map<String, dynamic> json) {
  return User(
    name: json['name'] as String,
    age: json['age'] as int,
    email: json['address'] as String,
  );
}

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'age': instance.age,
      'address': instance.email,
    };

如果只是命名上 Snake case 與 Camel case 的差別,可以透過 @JsonSerializable(fieldRename: FieldRename.snake) 來標註,就不用逐一在各欄位上使用 @JsonKey 標註名稱了,其他還有可設置的特性,詳情可查看 json_serializable 的文件。

如果你持續地在修改模型,也可以執行 flutter package pub run build_runner watch,這會持續監看模型的內容是否有更動,自動產生新的程式碼。