| // 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_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/int64_serializer.dart'; |
| import 'package:built_value/src/json_object_serializer.dart'; |
| import 'package:built_value/src/num_serializer.dart'; |
| import 'package:built_value/src/uri_serializer.dart'; |
| import 'package:quiver/core.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/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<Type> 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(BuiltListSerializer()) |
| ..add(BuiltListMultimapSerializer()) |
| ..add(BuiltMapSerializer()) |
| ..add(BuiltSetSerializer()) |
| ..add(BuiltSetMultimapSerializer()) |
| ..add(DateTimeSerializer()) |
| ..add(DoubleSerializer()) |
| ..add(DurationSerializer()) |
| ..add(IntSerializer()) |
| ..add(Int64Serializer()) |
| ..add(JsonObjectSerializer()) |
| ..add(NumSerializer()) |
| ..add(RegExpSerializer()) |
| ..add(StringSerializer()) |
| ..add(UriSerializer()) |
| ..addBuilderFactory(const FullType(BuiltList, [FullType.object]), |
| () => ListBuilder<Object>()) |
| ..addBuilderFactory( |
| const FullType( |
| BuiltListMultimap, [FullType.object, FullType.object]), |
| () => ListMultimapBuilder<Object, Object>()) |
| ..addBuilderFactory( |
| const FullType(BuiltMap, [FullType.object, FullType.object]), |
| () => MapBuilder<Object, Object>()) |
| ..addBuilderFactory(const FullType(BuiltSet, [FullType.object]), |
| () => SetBuilder<Object>()) |
| ..addBuilderFactory( |
| const FullType( |
| BuiltSetMultimap, [FullType.object, FullType.object]), |
| () => SetMultimapBuilder<Object, Object>())) |
| .build(); |
| } |
| |
| /// The installed [Serializer]s. |
| Iterable<Serializer> get serializers; |
| |
| /// 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<T>(Serializer<T> 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<T>(Serializer<T> serializer, Object 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<int, String>`, returns a |
| /// `ListBuilder<int, String>`. 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); |
| |
| /// The installed builder factories. |
| BuiltMap<FullType, Function> get builderFactories; |
| |
| 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); |
| |
| /// Adds an iterable of [Serializer]. |
| void addAll(Iterable<Serializer> serializers); |
| |
| /// Adds a builder factory. |
| /// |
| /// Builder factories are needed when deserializing to types that use |
| /// generics. For example, to deserialize a `BuiltList<Foo>`, `built_value` |
| /// needs a builder factory for `BuiltList<Foo>`. |
| /// |
| /// `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<Foo>(), |
| /// )) |
| /// .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<FullType> parameters; |
| |
| const FullType(this.root, [this.parameters = const []]); |
| |
| bool get isUnspecified => identical(root, null); |
| |
| @override |
| bool operator ==(dynamic other) { |
| if (identical(other, this)) return true; |
| if (other is! FullType) return false; |
| if (root != other.root) 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)); |
| } |
| |
| @override |
| String toString() => isUnspecified |
| ? 'unspecified' |
| : parameters.isEmpty |
| ? _getRawName(root) |
| : '${_getRawName(root)}<${parameters.join(", ")}>'; |
| |
| 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<T> { |
| /// 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<Type> 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<T> implements Serializer<T> { |
| /// 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, or a String. |
| /// |
| /// 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<T> implements Serializer<T> { |
| /// 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. |
| 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 '$json' to '$type' failed due to: $error"; |
| } |