[Flutter翻譯]使用BuiltValueSerializer建立自定義built_value序列化器

原文地址: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呢?用一個CustomSerializeide

若是咱們檢查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";
複製代碼

最後,咱們必須實現serializedeserialize方法。在這裏,咱們將可以檢查咱們接收的值是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(免費版)翻譯

相關文章
相關標籤/搜索