原文地址:medium.com/flutter-com…git
原文做者:medium.com/@solid.gonc…github
發佈時間:2019年2月9日 - 3分鐘閱讀shell
照片由Patrick Tomasso on Unsplash提供。json
當使用built_value進行JSON序列化和反序列化時,咱們可能會遇到一些超出StandardJsonPlugin
能力的狀況。數組
想象一下下面的問題:你有一個API端點,能夠爲同一個值提供兩種不一樣類型的數據結構,以下例所示:markdown
{ "value": "String value" } 複製代碼
{ "value": ["String 1", "String 2"] } 複製代碼
在咱們的flutter應用中,咱們可能但願有一個能夠同時擁有這兩種值的對象,而後咱們決定在咱們的Widgets中顯示什麼。數據結構
class CustomValue { String singleValue; List<String> multipleValues; } 複製代碼
那麼,咱們如何將String值映射爲singleValue
,將Strings數組映射爲multiValues
呢?用一個CustomSerialize
。ide
若是咱們檢查Serializer類,就會發現:oop
must extend either [PrimitiveSerializer] or/
[StructuredSerializer]./
複製代碼
因爲咱們的數據結構不是一個原始對象 咱們必須建立一個實現StructuredSerializer
的類。單元測試
class CustomValueSerializer implements StructuredSerializer<CustomValue> { @override CustomValue deserialize(Serializers serializers, Iterable serialized, {FullType specifiedType = FullType.unspecified}) { // TODO: implement deserialize return null; } @override Iterable serialize(Serializers serializers, CustomValue object, {FullType specifiedType = FullType.unspecified}) { // TODO: implement serialize return null; } @override // TODO: implement types Iterable<Type> get types => null; @override // TODO: implement wireName String get wireName => null; } 複製代碼
讓咱們檢查一下咱們須要實現的每一個方法。 types
是能夠被序列化的對象的類型。當使用built_value
時,它會生成一個名爲_$CustomValue
的內部類型,這個類型也必須被序列化,因此咱們有:
@override Iterable<Type> get types => [CustomValue, _$CustomValue]; 複製代碼
wirename
是咱們要序列化的類的名字。
@override String get wireName => "CustomValue"; 複製代碼
最後,咱們必須實現serialize
和deserialize
方法。在這裏,咱們將可以檢查咱們接收的值是String
類型仍是List
類型,並將其映射到CustomValue
的正確值。要作到這一點,咱們須要檢查相似類的生成代碼是如何結構的,並調整它以適應咱們的需求。在這種狀況下,咱們在檢查value
字段時,並非直接賦值,而是先檢查該變量的屬性是哪一種類型,將其賦爲String
值或List<String>
值。 可是,因爲咱們使用的是built_value
,咱們要處理的List類型來自於built_collection包,所以咱們要將其聲明爲BuiltList<String>
值
@override CustomValue deserialize(Serializers serializers, Iterable serialized, {FullType specifiedType = FullType.unspecified}) { // Initialize an empty builder final result = new CustomValueBuilder(); // Create an `Iterator` from the serialized data received final iterator = serialized.iterator; // Loop the iterator for each key while (iterator.moveNext()) { final key = iterator.current as String; iterator.moveNext(); final dynamic value = iterator.current; // for each key, assign the correct value to the builder switch (key) { case 'value': // If the value is of type List<dynamic>, assign it to `values` if (value is List<dynamic>) { result.values.replace(serializers.deserialize(value, specifiedType: const FullType(BuiltList, const [ const FullType(String) ])) as BuiltList); // else, the value is of type `String` } else { result.value = serializers.deserialize(value.toString(), specifiedType: const FullType(String)) as String; } break; } } return result.build(); } @override Iterable serialize(Serializers serializers, CustomValue object, {FullType specifiedType = FullType.unspecified}) { // Create an empty object array final result = <Object>[]; // if the value of the `CustomValue` is not null, then assign it to a String if (object.value != null) { result ..add('value') ..add(serializers.serialize(object.value, specifiedType: const FullType(String))); } // Else, it means that we have a list. In this case the list will always override // the defined String value if (object.values != null) { result ..add('values') ..add(serializers.serialize(object.values, specifiedType: const FullType(BuiltList, const [const FullType(String)]))); } return result; } 複製代碼
如今,既然咱們有了CustomValueSerializer
類,咱們就能夠開始研究CustomValue
類了。
part 'custom_value.g.dart'; abstract class CustomValue implements Built<CustomValue, CustomValueBuilder> { static Serializer<CustomValue> get serializer => null; // todo @nullable String get value; @nullable BuiltList<String> get values; CustomValue._(); factory CustomValue([updates(CustomValueBuilder b)]) = _$CustomValue; } 複製代碼
該類的設置等於使用StandardJsonPlugin
的類,惟一不一樣的是咱們聲明序列化器的方式。在這種狀況下,咱們可使用新的註解@BuiltValueSerializer
來對序列化器說:"嘿,咱們使用的是自定義序列化器,不要爲這個類生成一個"
@BuiltValueSerializer(custom: true) static Serializer<CustomValue> get serializer => CustomDataSerializer(); 複製代碼
缺乏了什麼?
咱們的Serializers
類,它聲明瞭項目中全部要序列化的類。對於自定義的序列化器,咱們不須要在這個類中添加額外的信息,因此咱們能夠像一般那樣初始化它。
part 'serializers.g.dart'; @SerializersFor(const [ CustomValue ]) Serializers serializers = _$serializers; Serializers standardSerializers = (serializers.toBuilder() ..addPlugin(StandardJsonPlugin()) ).build(); 複製代碼
最後,咱們能夠在終端中運行 build_runner
來生成全部的新文件。
flutter packages pub run build_runner watch
就這樣! 咱們已經成功地使用了built_value的自定義序列器! 🎉
做爲獎勵,咱們能夠經過編寫一些老式的單元測試來保證一切正常工做。
test("Single value", () { var value = "test"; var jsonMap = '{"value": "$value"}'; var encodedJson = json.jsonDecode(jsonMap); CustomValue customValue = standardSerializers.deserializeWith(CustomValue.serializer, encodedJson); expect(customValue.value, equals(value)); expect(customValue.values, isNull); }); test("Multiple values", () { var value1 = "test"; var value2 = "system"; var value = '["$value1", "$value2"]'; var jsonMap = '{"value": $value}'; var encodedJson = json.jsonDecode(jsonMap); CustomValue customValue = standardSerializers.deserializeWith(CustomValue.serializer, encodedJson); expect(customValue.value, isNull); expect(customValue.values, equals([value1, value2])); }); 複製代碼
全部測試都經過了,咱們就能夠開始了。
你能夠在 GitHub Repo 中看到完整的例子。
經過www.DeepL.com/Translator(免費版)翻譯