// 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 'package:built_collection/built_collection.dart'; import 'package:built_collection/src/internal/hash.dart'; import 'package:built_value/src/big_int_serializer.dart'; import 'package:built_value/src/date_time_serializer.dart'; import 'package:built_value/src/duration_serializer.dart'; import 'package:built_value/src/int32_serializer.dart'; import 'package:built_value/src/int64_serializer.dart'; import 'package:built_value/src/json_object_serializer.dart'; import 'package:built_value/src/list_serializer.dart'; import 'package:built_value/src/map_serializer.dart'; import 'package:built_value/src/num_serializer.dart'; import 'package:built_value/src/set_serializer.dart'; import 'package:built_value/src/uint8_list_serializer.dart'; import 'package:built_value/src/uri_serializer.dart'; import 'src/bool_serializer.dart'; import 'src/built_json_serializers.dart'; import 'src/built_list_multimap_serializer.dart'; import 'src/built_list_serializer.dart'; import 'src/built_map_serializer.dart'; import 'src/built_set_multimap_serializer.dart'; import 'src/built_set_serializer.dart'; import 'src/double_serializer.dart'; import 'src/int_serializer.dart'; import 'src/null_serializer.dart'; import 'src/regexp_serializer.dart'; import 'src/string_serializer.dart'; /// Annotation to trigger code generation of a [Serializers] instance. /// /// Use like this: /// /// ``` /// @SerializersFor(const [ /// MySerializableClass, /// MyOtherSerializableClass, /// ]) /// final Serializers serializers = _$serializers; /// ``` /// /// The `_$serializers` value will be generated for you in a part file next /// to the current source file. It will hold serializers for the types /// specified plus any types used in their fields, transitively. class SerializersFor { final List types; const SerializersFor(this.types); } /// Serializes all known classes. /// /// See: https://github.com/google/built_value.dart/tree/master/example abstract class Serializers { /// Default [Serializers] that can serialize primitives and collections. /// /// Use [toBuilder] to add more serializers. factory Serializers() { return (SerializersBuilder() ..add(BigIntSerializer()) ..add(BoolSerializer()) ..add(ListSerializer()) ..add(MapSerializer()) ..add(SetSerializer()) ..add(BuiltListSerializer()) ..add(BuiltListMultimapSerializer()) ..add(BuiltMapSerializer()) ..add(BuiltSetSerializer()) ..add(BuiltSetMultimapSerializer()) ..add(DateTimeSerializer()) ..add(DoubleSerializer()) ..add(DurationSerializer()) ..add(IntSerializer()) ..add(Int32Serializer()) ..add(Int64Serializer()) ..add(JsonObjectSerializer()) ..add(NullSerializer()) ..add(NumSerializer()) ..add(RegExpSerializer()) ..add(StringSerializer()) ..add(Uint8ListSerializer()) ..add(UriSerializer()) ..addBuilderFactory(const FullType(BuiltList, [FullType.object]), () => ListBuilder()) ..addBuilderFactory( const FullType( BuiltListMultimap, [FullType.object, FullType.object]), () => ListMultimapBuilder()) ..addBuilderFactory( const FullType(BuiltMap, [FullType.object, FullType.object]), () => MapBuilder()) ..addBuilderFactory(const FullType(BuiltSet, [FullType.object]), () => SetBuilder()) ..addBuilderFactory( const FullType( BuiltSetMultimap, [FullType.object, FullType.object]), () => SetMultimapBuilder())) .build(); } /// Merges iterable of [Serializers] into a single [Serializers]. /// /// [Serializer] and builder factories are accumulated. Plugins are not. static Serializers merge(Iterable serializersIterable) => (Serializers().toBuilder()..mergeAll(serializersIterable)).build(); /// The installed [Serializer]s. Iterable get serializers; /// The installed builder factories. BuiltMap get builderFactories; /// The installed serializer plugins. Iterable get serializerPlugins; /// Serializes [object]. /// /// A [Serializer] must have been provided for every type the object uses. /// /// Types that are known statically can be provided via [specifiedType]. This /// will reduce the amount of data needed on the wire. The exact same /// [specifiedType] will be needed to deserialize. /// /// Create one using [SerializersBuilder]. /// /// TODO(davidmorgan): document the wire format. Object? serialize(Object? object, {FullType specifiedType = FullType.unspecified}); /// Convenience method for when you know the type you're serializing. /// Specify the type by specifying its [Serializer] class. Equivalent to /// calling [serialize] with a `specifiedType`. Object? serializeWith(Serializer serializer, T? object); /// Convenience method for when you want a JSON string and know the type /// you're serializing. Specify the type by specifying its [Serializer] /// class. Equivalent to calling [serialize] with a `specifiedType` then /// calling `json.encode`. String toJson(Serializer serializer, T? object); /// Deserializes [serialized]. /// /// A [Serializer] must have been provided for every type the object uses. /// /// If [serialized] was produced by calling [serialize] with [specifiedType], /// the exact same [specifiedType] must be provided to deserialize. Object? deserialize(Object? serialized, {FullType specifiedType = FullType.unspecified}); /// Convenience method for when you know the type you're deserializing. /// Specify the type by specifying its [Serializer] class. Equivalent to /// calling [deserialize] with a `specifiedType`. T? deserializeWith(Serializer serializer, Object? serialized); /// Convenience method for when you have a JSON string and know the type /// you're deserializing. Specify the type by specifying its [Serializer] /// class. Equivalent to calling [deserialize] with a `specifiedType` then /// calling `json.decode`. T? fromJson(Serializer serializer, String serialized); /// Gets a serializer; returns `null` if none is found. For use in plugins /// and other extension code. Serializer? serializerForType(Type type); /// Gets a serializer; returns `null` if none is found. For use in plugins /// and other extension code. Serializer? serializerForWireName(String wireName); /// Creates a new builder for the type represented by [fullType]. /// /// For example, if [fullType] is `BuiltList`, returns a /// `ListBuilder`. This helps serializers to instantiate with /// correct generic type parameters. /// /// Throws a [StateError] if no matching builder factory has been added. Object newBuilder(FullType fullType); /// Whether a builder for [fullType] is available via [newBuilder]. bool hasBuilder(FullType fullType); /// Throws if a builder for [fullType] is not available via [newBuilder]. void expectBuilder(FullType fullType); SerializersBuilder toBuilder(); } /// Note: this is an experimental feature. API may change without a major /// version increase. abstract class SerializerPlugin { Object? beforeSerialize(Object? object, FullType specifiedType); Object? afterSerialize(Object? object, FullType specifiedType); Object? beforeDeserialize(Object? object, FullType specifiedType); Object? afterDeserialize(Object? object, FullType specifiedType); } /// Builder for [Serializers]. abstract class SerializersBuilder { factory SerializersBuilder() = BuiltJsonSerializersBuilder; /// Adds a [Serializer]. It will be used to handle the type(s) it declares /// via its `types` property. void add(Serializer serializer); /// Merges a [Serializers], adding all of its [Serializer] instances and /// builder factories. Does _not_ add plugins. void merge(Serializers serializers); /// Adds an iterable of [Serializer]. void addAll(Iterable serializers); /// Merges an iterable of [Serializers]. void mergeAll(Iterable serializersIterable); /// Adds a builder factory. /// /// Builder factories are needed when deserializing to types that use /// generics. For example, to deserialize a `BuiltList`, `built_value` /// needs a builder factory for `BuiltList`. /// /// `built_value` tries to generate code that will install all the builder /// factories you need, but this support is incomplete. So you may need to /// add your own. For example: /// /// ```dart /// serializers = (serializers.toBuilder() /// ..addBuilderFactory( /// const FullType(BuiltList, [FullType(Foo)]), /// () => ListBuilder(), /// )) /// .build(); /// ``` void addBuilderFactory(FullType specifiedType, Function function); /// Installs a [SerializerPlugin] that applies to all serialization and /// deserialization. void addPlugin(SerializerPlugin plugin); Serializers build(); } /// A [Type] with, optionally, [FullType] generic type parameters. /// /// May also be [unspecified], indicating that no type information is /// available. class FullType { /// An unspecified type. static const FullType unspecified = FullType(null); /// The [Object] type. static const FullType object = FullType(Object); /// The root of the type. final Type? root; /// Type parameters of the type. final List parameters; /// Whether the type is nullable. final bool nullable; const FullType(this.root, [this.parameters = const []]) : nullable = false; const FullType.nullable(this.root, [this.parameters = const []]) : nullable = true; bool get isUnspecified => identical(root, null); FullType withNullability(bool nullability) => nullability ? FullType.nullable(root, parameters) : FullType(root, parameters); @override bool operator ==(Object other) { if (identical(other, this)) return true; if (other is! FullType) return false; if (root != other.root) return false; if (nullable != other.nullable) return false; if (parameters.length != other.parameters.length) return false; for (var i = 0; i != parameters.length; ++i) { if (parameters[i] != other.parameters[i]) return false; } return true; } @override int get hashCode { return hash2(root, hashObjects(parameters)) ^ (nullable ? 0x696eefd9 : 0); } @override String toString() => isUnspecified ? 'unspecified' : (parameters.isEmpty ? _getRawName(root) : '${_getRawName(root)}<${parameters.join(", ")}>') + _nullabilitySuffix; String get _nullabilitySuffix => nullable ? '?' : ''; static String _getRawName(Type? type) { var name = type.toString(); var genericsStart = name.indexOf('<'); return genericsStart == -1 ? name : name.substring(0, genericsStart); } } /// Serializes a single type. /// /// You should not usually need to implement this interface. Implementations /// are provided for collections and primitives in `built_json`. Classes using /// `built_value` and enums using `EnumClass` can have implementations /// generated using `built_json_generator`. /// /// Implementations must extend either [PrimitiveSerializer] or /// [StructuredSerializer]. abstract class Serializer { /// The [Type]s that can be serialized. /// /// They must all be equal to T or a subclass of T. Subclasses are used when /// T is an abstract class, which is the case with built_value generated /// serializers. Iterable get types; /// The wire name of the serializable type. For most classes, the class name. /// For primitives and collections a lower-case name is defined as part of /// the `built_json` wire format. String get wireName; } /// A [Serializer] that serializes to and from primitive JSON values. abstract class PrimitiveSerializer implements Serializer { /// Serializes [object]. /// /// Use [serializers] as needed for nested serialization. Information about /// 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, a String or a List. /// /// TODO(davidmorgan): document the wire format. Object serialize(Serializers serializers, T object, {FullType specifiedType = FullType.unspecified}); /// Deserializes [serialized]. /// /// [serialized] is a boolean, an integer, a double or a String. /// /// Use [serializers] as needed for nested deserialization. Information about /// the type being deserialized is provided in [specifiedType]. T deserialize(Serializers serializers, Object serialized, {FullType specifiedType = FullType.unspecified}); } /// A [Serializer] that serializes to and from an [Iterable] of primitive JSON /// values. abstract class StructuredSerializer implements Serializer { /// Serializes [object]. /// /// Use [serializers] as needed for nested serialization. Information about /// the type being serialized is provided in [specifiedType]. /// /// Returns an [Iterable] of values that can be represented as structured /// JSON: booleans, integers, doubles, Strings and [Iterable]s. /// /// TODO(davidmorgan): document the wire format. Iterable serialize(Serializers serializers, T object, {FullType specifiedType = FullType.unspecified}); /// Deserializes [serialized]. /// /// [serialized] is an [Iterable] that may contain booleans, integers, /// doubles, Strings and/or [Iterable]s. /// /// Use [serializers] as needed for nested deserialization. Information about /// the type being deserialized is provided in [specifiedType]. T deserialize(Serializers serializers, Iterable serialized, {FullType specifiedType = FullType.unspecified}); } /// [Error] conveying why deserialization failed. /// /// The Object that failed to deseralize is included as [json] but is not displayed /// in the error message to prevent accidental inclusion in logs. class DeserializationError extends Error { final String? json; final FullType type; final Error error; factory DeserializationError(Object? json, FullType type, Error error) { var limitedJson = json.toString(); if (limitedJson.length > 80) { limitedJson = limitedJson.replaceRange(77, limitedJson.length, '...'); } return DeserializationError._(limitedJson, type, error); } DeserializationError._(this.json, this.type, this.error); @override String toString() => "Deserializing to '$type' failed due to: $error"; }