Skip to content

Commit

Permalink
feat: support ignoring custom types that StandardJsonPlugin should le…
Browse files Browse the repository at this point in the history
…ave as lists (#1296)

Signed-off-by: Nikolas Rimikis <[email protected]>
Co-authored-by: Nikolas Rimikis <[email protected]>
  • Loading branch information
Leptopoda and Leptopoda authored Jan 10, 2024
1 parent 8b3dd1b commit 861fadb
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 12 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

# 8.9.0-wip

- Add ignoring types that the StandardJsonPlugin should leave as a List.

# 8.8.1

- Fix codegen for enum wire keys when there is a `$` in the field name.
Expand Down
2 changes: 1 addition & 1 deletion built_value/lib/serializer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ abstract class PrimitiveSerializer<T> implements Serializer<T> {
/// the type being serialized is provided in [specifiedType].
///
/// Returns a value that can be represented as a JSON primitive: a boolean,
/// an integer, a double, or a String.
/// an integer, a double, a String or a List.
///
/// TODO(davidmorgan): document the wire format.
Object serialize(Serializers serializers, T object,
Expand Down
19 changes: 12 additions & 7 deletions built_value/lib/standard_json_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ class StandardJsonPlugin implements SerializerPlugin {
/// The field used to specify the value type if needed. Defaults to `$`.
final String discriminator;

// The key used when there is just a single value, for example if serializing
// an `int`.
/// The key used when there is just a single value, for example if serializing
/// an `int`.
final String valueKey;

StandardJsonPlugin({this.discriminator = r'$', this.valueKey = ''});
/// The types to leave as a list when converting into json.
final BuiltSet<Type> typesToLeaveAsList;

StandardJsonPlugin({
this.discriminator = r'$',
this.valueKey = '',
Iterable<Type>? typesToLeaveAsList,
}) : typesToLeaveAsList = BuiltSet<Type>(
{BuiltList, BuiltSet, JsonObject, ...?typesToLeaveAsList});

@override
Object? beforeSerialize(Object? object, FullType specifiedType) {
Expand All @@ -41,10 +49,7 @@ class StandardJsonPlugin implements SerializerPlugin {

@override
Object? afterSerialize(Object? object, FullType specifiedType) {
if (object is List &&
specifiedType.root != BuiltList &&
specifiedType.root != BuiltSet &&
specifiedType.root != JsonObject) {
if (object is List && !typesToLeaveAsList.contains(specifiedType.root)) {
if (specifiedType.isUnspecified) {
return _toMapWithDiscriminator(object);
} else {
Expand Down
56 changes: 56 additions & 0 deletions end_to_end_test/lib/records.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ abstract class SerializableRecordValue

int get value;
RecordOfIntInt? get record;
RecordOfIntOrList? get intOrList;

factory SerializableRecordValue(
[void Function(SerializableRecordValueBuilder) updates]) =
Expand Down Expand Up @@ -93,3 +94,58 @@ class RecordOfIntIntSerializer implements StructuredSerializer<RecordOfIntInt> {
@override
String get wireName => 'RecordOfIntInt';
}

typedef RecordOfIntOrList = (int?, BuiltList<String>?);

class RecordOfIntOrListSerializer
implements PrimitiveSerializer<RecordOfIntOrList> {
const RecordOfIntOrListSerializer();

@override
Iterable<Type> get types => const [RecordOfIntOrList];

@override
String get wireName => 'RecordOfIntOrList';

@override
Object serialize(
Serializers serializers,
RecordOfIntOrList object, {
FullType specifiedType = FullType.unspecified,
}) {
dynamic value;
value = object.$1;
if (value != null) {
return serializers.serialize(value, specifiedType: const FullType(int))!;
}
value = object.$2;
if (value != null) {
return serializers.serialize(value,
specifiedType: const FullType(BuiltList, [FullType(String)]))!;
}

throw StateError('Tried to serialize without any value.');
}

@override
RecordOfIntOrList deserialize(
Serializers serializers,
Object data, {
FullType specifiedType = FullType.unspecified,
}) {
BuiltList<String>? list;
try {
list = serializers.deserialize(
data,
specifiedType: const FullType(BuiltList, [FullType(String)]),
)! as BuiltList<String>;
} catch (_) {}
int? number;
try {
number = serializers.deserialize(data,
specifiedType: const FullType(int))! as int;
} catch (_) {}

return (number, list);
}
}
33 changes: 29 additions & 4 deletions end_to_end_test/lib/records.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions end_to_end_test/test/records_serializer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import 'dart:convert';

import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:end_to_end_test/errors_matchers.dart';
import 'package:end_to_end_test/records.dart';
import 'package:end_to_end_test/serializers.dart';
Expand Down Expand Up @@ -63,4 +66,33 @@ void main() {
expect(serializersWithCustomSerializer.deserialize(serialized), data);
});
});

group('$SerializableRecordValue with a record list value', () {
var data = SerializableRecordValue((b) => b
..value = 1
..intOrList = (null, BuiltList(['value0', 'value1', 'value2'])));
var serialized = json.decode(json.encode({
'value': 1,
'intOrList': ['value0', 'value1', 'value2'],
})) as Object;
var serializersWithCustomSerializer = (serializers.toBuilder()
..addPlugin(
StandardJsonPlugin(typesToLeaveAsList: {RecordOfIntOrList}))
..add(RecordOfIntOrListSerializer()))
.build();

test('can be serialized with custom serializer', () {
expect(
serializersWithCustomSerializer.serialize(data,
specifiedType: FullType(SerializableRecordValue)),
serialized);
});

test('can be deserialized with custom deserializer', () {
expect(
serializersWithCustomSerializer.deserialize(serialized,
specifiedType: FullType(SerializableRecordValue)),
data);
});
});
}

0 comments on commit 861fadb

Please sign in to comment.