Serialisasi ke JSON dan objek yang tidak berubah. Tentang paket built_value untuk Flutter





Terkadang JSON dari API perlu diubah menjadi objek dan sebaiknya nilai yang tidak berubah. Pada Dart itu mungkin, tetapi membutuhkan banyak pengkodean untuk masing-masing objek. Untungnya, ada paket yang akan membantu Anda melakukan semua ini, dan dalam artikel ini saya akan memberi tahu Anda tentang metode ini.



Tujuan kami:



1. Serialisasi



final user = User.fromJson({"name": "Maks"});
final json = user.toJson();


2. Gunakan sebagai nilai



final user1 = User.fromJson({"name": "Maks"});
final user2 = User((b) => b..name='Maks');
if (user1 == user2) print('    ');


3. Kekekalan



user.name = 'Alex'; // 
final newUser = user.rebuild((b) => b..name='Alex'); // 




Instal paket



Buka file pubspec.yaml di proyek Flutter kami dan tambahkan paket built_value ke dependensi :



  ...
  built_value: ^7.1.0


Dan juga tambahkan paket built_value_generator dan build_runner ke dev_dependencies . Paket-paket ini akan membantu Anda menghasilkan kode yang diperlukan.



dev_dependencies :



 ...
  build_runner: ^1.10.2
  built_value_generator: ^7.1.0


Simpan file pubspec.yaml dan jalankan " flutter pub get " untuk mendapatkan semua paket yang diperlukan.



Buat nilai_bangun



Mari buat kelas sederhana untuk melihat cara kerjanya.



Buat file baru user.dart :



import 'package:built_value/built_value.dart';

part 'user.g.dart';

abstract class User implements Built<User, UserBuilder> {
  String get name;

  User._();
  factory User([void Function(UserBuilder) updates]) = _$User;
}


Jadi, kami membuat kelas Pengguna abstrak sederhana dengan satu bidang nama , menunjukkan bahwa kelas kami adalah bagian dari user.g.dart dan implementasi utamanya ada di sana, termasuk UserBuilder . Untuk membuat file ini secara otomatis, Anda perlu menjalankannya di baris perintah:



flutter packages pub run build_runner watch


atau



flutter packages pub run build_runner build


Kami mulai dengan jam tangan agar tidak memulai ulang setiap kali ada perubahan di kelas.



Setelah itu, kita melihat bahwa file user.g.dart baru telah muncul . Anda dapat melihat apa yang ada di dalamnya dan berapa banyak waktu yang akan kami hemat dengan mengotomatiskan proses ini. Ketika kami menambahkan lebih banyak bidang dan serialisasi, file ini akan menjadi lebih besar.



Mari kita periksa apa yang kita dapatkan:



final user = User((b) => b..name = "Max");
print(user);
print(user == User((b) => b..name = "Max")); // true
print(user == User((b) => b..name = "Alex")); // false


nullable



Tambahkan bidang nama keluarga baru ke kelas Pengguna :



abstract class User implements Built<User, UserBuilder> {
  String get name;
  String get surname;
...
}


Jika Anda mencoba seperti ini:



final user = User((b) => b..name = 'Max');


Kemudian kami mendapatkan kesalahan:



Tried to construct class "User" with null field "surname".


Untuk membuat nama keluarga opsional, gunakannullable:



@nullable
String get surname;


atau Anda perlu memberikan nama belakang setiap kali :



final user = User((b) => b
  ..name = 'Max'
  ..surname = 'Madov');
print(user);


Koleksi yang Dibangun



Mari gunakan array. BuiltList akan membantu kami untuk ini :



import 'package:built_collection/built_collection.dart';
...
abstract class User implements Built<User, UserBuilder> {
  ...
  @nullable
  BuiltList<String> get rights;
...


final user = User((b) => b
  ..name = 'Max'
  ..rights.addAll(['read', 'write']));
print(user);


Enum



Perlu untuk membatasi hak agar tidak mengambil nilai lain selain ' read ', ' write ' dan ' delete '. Untuk melakukan ini, buat file baru bernama right.dart buat EnumClass baru :



import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';

part 'right.g.dart';

class Right extends EnumClass {
  static const Right read = _$read;
  static const Right write = _$write;
  static const Right delete = _$delete;

  const Right._(String name) : super(name);

  static BuiltSet<Right> get values => _$rightValues;
  static Right valueOf(String name) => _$rightValueOf(name);
}


Pengguna:



@nullable
BuiltList<Right> get rights;


Sekarang hak hanya menerima jenis yang Tepat :



final user = User((b) => b
  ..name = 'Max'
  ..rights.addAll([Right.read, Right.write]));
print(user);


Serialisasi



Agar objek ini dapat dengan mudah diubah menjadi JSON dan kembali, kita perlu menambahkan beberapa metode lagi ke kelas kita:



...
import 'package:built_value/serializer.dart';
import 'serializers.dart';
...
abstract class User implements Built<User, UserBuilder> {
...
  Map<String, dynamic> toJson() => serializers.serializeWith(User.serializer, this);
  static User fromJson(Map<String, dynamic> json) =>
serializers.deserializeWith(User.serializer, json);

  static Serializer<User> get serializer => _$userSerializer;
}


Pada prinsipnya, ini cukup untuk serialisasi:



static Serializer<User> get serializer => _$userSerializer;


Tetapi untuk kenyamanan, mari tambahkan metode toJson dan fromJson .



Kami juga menambahkan satu baris ke kelas Kanan:



import 'package:built_value/serializer.dart';
,,,
class Right extends EnumClass {
...
  static Serializer<Right> get serializer => _$rightSerializer;
}


Dan Anda perlu membuat file lain bernama serializers.dart :



import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:built_value_demo/right.dart';
import 'package:built_value_demo/user.dart';

part 'serializers.g.dart';

@SerializersFor([Right, User])
final Serializers serializers =
(_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();


Setiap kelas Built baru perlu ditambahkan ke @SerializersFor ([ ... ]) agar serialisasi berfungsi seperti yang diharapkan .



Sekarang kami dapat memeriksa apa yang kami dapatkan:



final user = User.fromJson({
  "name": "Max",
  "rights": ["read", "write"]
});
print(user);
print(user.toJson());


final user2 = User((b) => b
  ..name = 'Max'
  ..rights.addAll([Right.read, Right.write]));
print(user == user2); // true


Mari kita ubah nilainya:



final user3 = user.rebuild((b) => b
  ..surname = "Madov"
  ..rights.replace([Right.read]));
print(user3);


Selain itu



Alhasil, akan ada yang mengatakan masih perlu menulis cukup banyak. Tetapi jika Anda menggunakan Visual Studio Code, saya sarankan menginstal potongan bernama Built Value Snippets dan kemudian Anda dapat menghasilkan semua ini secara otomatis. Untuk melakukan ini, telusuri Marketplace atau ikuti tautan ini .



Setelah instalasi, tulis " bv " di file Dart dan Anda dapat melihat opsi apa yang ada.



Jika Anda tidak ingin Visual Studio Code menampilkan file " * .g.dart " yang dihasilkan , Anda perlu membuka Pengaturan dan mencari Files: Exclude , lalu klik Add Pattern dan tambahkan "** / *. g.dart ”.



Apa berikutnya?



Pada pandangan pertama, tampaknya usaha yang begitu banyak tidak sepadan, tetapi jika Anda memiliki banyak kelas seperti itu, ini akan sangat memudahkan dan mempercepat keseluruhan proses.



NB Saya akan sangat senang dan berterima kasih kepada Anda jika Anda mau membagikan metode Anda, yang menurut Anda lebih praktis dan lebih efektif daripada yang saya usulkan. Paket



proyek GitHub



:

pub.dev/packages/built_value

pub.dev/packages/built_value_generator

pub.dev/packages/build_runner



All Articles