blob: 08b96c4df80c3c08a91e0a1a4ec56cc74dc2a820 [file] [log] [blame]
// Copyright (c) 2015, Google Inc. Please see the AUTHORS file for details.
// All rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import 'dart:convert';
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
/// Default implementation of [Serializers].
class BuiltJsonSerializers implements Serializers {
final BuiltMap<Type, Serializer> _typeToSerializer;
// Implementation note: wire name is what gets sent in the JSON, type name is
// the runtime type name. Type name is complicated for two reasons:
//
// 1. Built Value classes have two types, the abstract class and the
// generated implementation.
//
// 2. When compiled to javascript the runtime type names are obfuscated.
final BuiltMap<String, Serializer> _wireNameToSerializer;
final BuiltMap<String, Serializer> _typeNameToSerializer;
@override
final BuiltMap<FullType, Function> builderFactories;
@override
final BuiltList<SerializerPlugin> serializerPlugins;
BuiltJsonSerializers._(
this._typeToSerializer,
this._wireNameToSerializer,
this._typeNameToSerializer,
this.builderFactories,
this.serializerPlugins);
@override
Iterable<Serializer> get serializers => _wireNameToSerializer.values;
@override
T? deserializeWith<T>(Serializer<T> serializer, Object? serialized) {
return deserialize(serialized,
specifiedType: FullType(serializer.types.first)) as T?;
}
@override
T? fromJson<T>(Serializer<T> serializer, String serialized) {
return deserializeWith<T>(serializer, json.decode(serialized));
}
@override
Object? serializeWith<T>(Serializer<T> serializer, T? object) {
return serialize(object, specifiedType: FullType(serializer.types.first));
}
@override
String toJson<T>(Serializer<T> serializer, T? object) {
return json.encode(serializeWith<T>(serializer, object));
}
@override
Object? serialize(Object? object,
{FullType specifiedType = FullType.unspecified}) {
var transformedObject = object;
for (var plugin in serializerPlugins) {
transformedObject =
plugin.beforeSerialize(transformedObject, specifiedType);
}
var result = _serialize(transformedObject, specifiedType);
for (var plugin in serializerPlugins) {
result = plugin.afterSerialize(result, specifiedType);
}
return result;
}
Object? _serialize(Object? object, FullType specifiedType) {
if (specifiedType.isUnspecified) {
final serializer = serializerForType(object.runtimeType);
if (serializer == null) {
throw StateError("No serializer for '${object.runtimeType}'.");
}
if (serializer is StructuredSerializer) {
final result = <Object?>[serializer.wireName];
return result..addAll(serializer.serialize(this, object));
} else if (serializer is PrimitiveSerializer) {
return object == null
? <Object?>[serializer.wireName, null]
: <Object>[serializer.wireName, serializer.serialize(this, object)];
} else {
throw StateError(
'serializer must be StructuredSerializer or PrimitiveSerializer');
}
} else {
final serializer = serializerForType(specifiedType.root);
if (serializer == null) {
// Might be an interface; try resolving using the runtime type.
return serialize(object);
}
if (serializer is StructuredSerializer) {
return object == null
? null
: serializer
.serialize(this, object, specifiedType: specifiedType)
.toList();
} else if (serializer is PrimitiveSerializer) {
return object == null
? null
: serializer.serialize(this, object, specifiedType: specifiedType);
} else {
throw StateError(
'serializer must be StructuredSerializer or PrimitiveSerializer');
}
}
}
@override
Object? deserialize(Object? object,
{FullType specifiedType = FullType.unspecified}) {
var transformedObject = object;
for (var plugin in serializerPlugins) {
transformedObject =
plugin.beforeDeserialize(transformedObject, specifiedType);
}
var result = _deserialize(object, transformedObject, specifiedType);
for (var plugin in serializerPlugins) {
result = plugin.afterDeserialize(result, specifiedType);
}
return result;
}
Object? _deserialize(
Object? objectBeforePlugins, Object? object, FullType specifiedType) {
if (specifiedType.isUnspecified) {
final wireName = (object as List<Object?>).first as String;
final serializer = serializerForWireName(wireName);
if (serializer == null) {
throw StateError("No serializer for '$wireName'.");
}
if (serializer is StructuredSerializer) {
try {
return serializer.deserialize(this, object.sublist(1));
} on Error catch (error) {
throw DeserializationError(object, specifiedType, error);
}
} else if (serializer is PrimitiveSerializer) {
try {
var primitive = object[1];
return primitive == null
? null
: serializer.deserialize(this, primitive);
} on Error catch (error) {
throw DeserializationError(object, specifiedType, error);
}
} else {
throw StateError(
'serializer must be StructuredSerializer or PrimitiveSerializer');
}
} else {
final serializer = serializerForType(specifiedType.root);
if (serializer == null) {
if (object is List && object.first is String) {
// Might be an interface; try resolving using the type on the wire.
return deserialize(objectBeforePlugins);
} else {
throw StateError("No serializer for '${specifiedType.root}'.");
}
}
if (serializer is StructuredSerializer) {
try {
return object == null
? null
: serializer.deserialize(this, object as Iterable<Object?>,
specifiedType: specifiedType);
} on Error catch (error) {
throw DeserializationError(object, specifiedType, error);
}
} else if (serializer is PrimitiveSerializer) {
try {
return object == null
? null
: serializer.deserialize(this, object,
specifiedType: specifiedType);
} on Error catch (error) {
throw DeserializationError(object, specifiedType, error);
}
} else {
throw StateError(
'serializer must be StructuredSerializer or PrimitiveSerializer');
}
}
}
@override
Serializer? serializerForType(Type? type) =>
_typeToSerializer[type] ?? _typeNameToSerializer[_getRawName(type)];
@override
Serializer? serializerForWireName(String wireName) =>
_wireNameToSerializer[wireName];
@override
Object newBuilder(FullType fullType) {
var builderFactory = builderFactories[fullType];
if (builderFactory == null) _throwMissingBuilderFactory(fullType);
return builderFactory();
}
@override
void expectBuilder(FullType fullType) {
if (!hasBuilder(fullType)) _throwMissingBuilderFactory(fullType);
}
Never _throwMissingBuilderFactory(FullType fullType) {
throw StateError('No builder factory for $fullType. '
'Fix by adding one, see SerializersBuilder.addBuilderFactory.');
}
@override
bool hasBuilder(FullType fullType) {
return builderFactories.containsKey(fullType);
}
@override
SerializersBuilder toBuilder() {
return BuiltJsonSerializersBuilder._(
_typeToSerializer.toBuilder(),
_wireNameToSerializer.toBuilder(),
_typeNameToSerializer.toBuilder(),
builderFactories.toBuilder(),
serializerPlugins.toBuilder());
}
}
/// Default implementation of [SerializersBuilder].
class BuiltJsonSerializersBuilder implements SerializersBuilder {
final MapBuilder<Type, Serializer> _typeToSerializer;
final MapBuilder<String, Serializer> _wireNameToSerializer;
final MapBuilder<String, Serializer> _typeNameToSerializer;
final MapBuilder<FullType, Function> _builderFactories;
final ListBuilder<SerializerPlugin> _plugins;
factory BuiltJsonSerializersBuilder() => BuiltJsonSerializersBuilder._(
MapBuilder<Type, Serializer>(),
MapBuilder<String, Serializer>(),
MapBuilder<String, Serializer>(),
MapBuilder<FullType, Function>(),
ListBuilder<SerializerPlugin>());
BuiltJsonSerializersBuilder._(
this._typeToSerializer,
this._wireNameToSerializer,
this._typeNameToSerializer,
this._builderFactories,
this._plugins);
@override
void add(Serializer serializer) {
if (serializer is! StructuredSerializer &&
serializer is! PrimitiveSerializer) {
throw ArgumentError(
'serializer must be StructuredSerializer or PrimitiveSerializer');
}
_wireNameToSerializer[serializer.wireName] = serializer;
for (var type in serializer.types) {
_typeToSerializer[type] = serializer;
_typeNameToSerializer[_getRawName(type)] = serializer;
}
}
@override
void addAll(Iterable<Serializer> serializers) {
serializers.forEach(add);
}
@override
void addBuilderFactory(FullType types, Function function) {
_builderFactories[types] = function;
// Nullability of the top level type is irrelevant to serialization, but
// lookup might be done with either nullable or not nullable depending
// on the context. So, store both for fast lookup.
_builderFactories[types.withNullability(!types.nullable)] = function;
}
@override
void merge(Serializers serializers) {
addAll(serializers.serializers);
_builderFactories.addAll(serializers.builderFactories.asMap());
}
@override
void mergeAll(Iterable<Serializers> serializersIterable) {
for (var serializers in serializersIterable) {
merge(serializers);
}
}
@override
void addPlugin(SerializerPlugin plugin) {
_plugins.add(plugin);
}
@override
Serializers build() {
return BuiltJsonSerializers._(
_typeToSerializer.build(),
_wireNameToSerializer.build(),
_typeNameToSerializer.build(),
_builderFactories.build(),
_plugins.build());
}
}
String _getRawName(Type? type) {
var name = type.toString();
var genericsStart = name.indexOf('<');
return genericsStart == -1 ? name : name.substring(0, genericsStart);
}