I have an API which sends me a type :
type Response = {
id: string;
values: Stringified<number[]>, // string
}
where type Stringified<T> = string;
and is what you would get by using jsonEncode
.
I want to create a Built
class that matches the response architecture. This is what I have for now:
@SerializersFor([
MyResponse,
])
final Serializers _serializers = (_$_serializers.toBuilder()
..addPlugin(StandardJsonPlugin())
.build();
abstract class MyResponse implements Built<MyResponse, MyResponseBuilder> {
factory MyResponse([void Function(MyResponseBuilder) updates]) = _$MyResponse;
MyResponse._();
Map<String, dynamic> toJson() {
return _serializers.serializeWith(MyResponse.serializer, this)! as Map<String, dynamic>;
}
static MyResponse fromJson(Map<String, dynamic> json) {
return _serializers.deserializeWith(MyResponse.serializer, json)!;
}
static Serializer<MyResponse> get serializer => _$myResponseSerializer;
String get id;
List<int> get values;
}
This would work if I was receiving/sending data like :
{ "id": "id", "values": [0, 1, 2] }
But the data I receive or need to send is:
{ "id": "id", "values": "[0, 1, 2]" }
How can I override the toJson
/ fromJson
of values
or how can I add a pipe/how so I can use jsonEncode
/jsonDecode
when exporting to/importing from a JSON?
In the end, I managed to do it with a SerializerPlugin
:
Here is a more complete example with 2 models:
type Model1 {
boolean: boolean;
integer: number;
}
type Model2 = {
char: string;
list: Stringified<Model1[]>;
}
where type Stringified<T> = string;
and is what you would get by using jsonEncode
.
I created those built value classes:
// model_1.dart
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
part 'model_1.g.dart';
@SerializersFor([
Model1,
])
final Serializers _serializers =
(_$_serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
abstract class Model1 implements Built<Model1, Model1Builder> {
Model1._();
factory Model1([void Function(Model1Builder) updates]) = _$Model1;
Map<String, dynamic> toJson() {
return _serializers.serializeWith(Model1.serializer, this)
as Map<String, dynamic>;
}
static Model1 fromJson(Map<String, dynamic> json) {
return _serializers.deserializeWith(Model1.serializer, json)!;
}
static Serializer<Model1> get serializer => _$model1Serializer;
bool get boolean;
int get integer;
}
// model_2.dart
import 'dart:convert';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:flutter_app_stable/model_1.dart';
import 'package:flutter_app_stable/my_plugin.dart';
part 'model_2.g.dart';
@SerializersFor([
Model2,
Model1,
])
final Serializers _serializers = (_$_serializers.toBuilder()
..addPlugin(StandardJsonPlugin())
..addPlugin(MyPlugin()) // <- Add the plugin here.
..addBuilderFactory(
const FullType(BuiltList, [FullType(Model1)]),
ListBuilder<Model1>.new,
))
.build();
abstract class Model2 implements Built<Model2, Model2Builder> {
Model2._();
factory Model2([void Function(Model2Builder) updates]) = _$Model2;
Map<String, dynamic> toJson() {
return _serializers.serializeWith(Model2.serializer, this)
as Map<String, dynamic>;
}
static Model2 fromJson(Map<String, dynamic> json) {
return _serializers.deserializeWith(Model2.serializer, json)!;
}
static Serializer<Model2> get serializer => _$model2Serializer;
String get char;
BuiltList<Model1> get list;
}
// my_plugin.dart
class MyPlugin extends SerializerPlugin {
@override
Object? afterDeserialize(Object? object, FullType specifiedType) {
return object;
}
@override
Object? afterSerialize(Object? object, FullType specifiedType) {
if (specifiedType.root == BuiltList &&
specifiedType.parameters.length == 1 &&
specifiedType.parameters.first.root == Model1) {
return jsonEncode(object);
} else {
return object;
}
}
@override
Object? beforeDeserialize(Object? object, FullType specifiedType) {
if (specifiedType.root == BuiltList &&
specifiedType.parameters.length == 1 &&
specifiedType.parameters.first.root == Model1) {
return jsonDecode(object as String);
} else {
return object;
}
}
@override
Object? beforeSerialize(Object? object, FullType specifiedType) {
return object;
}
}
Notice the class MyPlugin extends SerializerPlugin
in model_2.dart
. I add this plugin to the Model2
serializer.
It
afterSerialize
that applies jsonEncode
to the serialized list of serialized model1beforeDeserialize
that applies jsonDecode
to the deserialized stringified list of serialized model1