釋出時間:2019年2月9日 - 3分鐘閱讀
照片由Patrick Tomasso on Unsplash提供。
當使用built_value進行JSON序列化和反序列化時,我們可能會遇到一些超出StandardJsonPlugin
能力的情況。
想象一下下面的問題:你有一個API端點,可以為同一個值提供兩種不同型別的資料結構,如下例所示:
{
"value": "String value"
}
複製程式碼
{
"value": ["String 1", "String 2"]
}
複製程式碼
在我們的flutter應用中,我們可能希望有一個可以同時擁有這兩種值的物件,然後我們決定在我們的Widgets中顯示什麼。
class CustomValue {
String singleValue;
List<String> multipleValues;
}
複製程式碼
那麼,我們如何將String值對映為singleValue
,將Strings陣列對映為multiValues
呢?用一個CustomSerialize
。
如果我們檢查Serializer類,就會發現:
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(免費版)翻譯