| @@ -24,15 +24,6 @@ namespace Discord.API | |||||
| { | { | ||||
| internal class DiscordRestApiClient : IDisposable | internal class DiscordRestApiClient : IDisposable | ||||
| { | { | ||||
| static DiscordRestApiClient() | |||||
| { | |||||
| SerializationFormat.Json.AddConverter<Image, ImagePropertyConverter>(); | |||||
| SerializationFormat.Json.AddConverter<long, Int53PropertyConverter>((type, prop) => prop?.GetCustomAttribute<Int53Attribute>() != null); | |||||
| SerializationFormat.Json.AddConverter<ulong, UInt53PropertyConverter>((type, prop) => prop?.GetCustomAttribute<Int53Attribute>() != null); | |||||
| SerializationFormat.Json.AddGenericConverter(typeof(EntityOrId<>), typeof(EntityOrIdPropertyConverter<>)); | |||||
| SerializationFormat.Json.AddGenericConverter(typeof(Optional<>), typeof(OptionalPropertyConverter<>)); | |||||
| } | |||||
| private static readonly ConcurrentDictionary<string, Func<BucketIds, string>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, string>>(); | private static readonly ConcurrentDictionary<string, Func<BucketIds, string>> _bucketIdGenerators = new ConcurrentDictionary<string, Func<BucketIds, string>>(); | ||||
| public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } | public event Func<string, string, double, Task> SentRequest { add { _sentRequestEvent.Add(value); } remove { _sentRequestEvent.Remove(value); } } | ||||
| @@ -1,4 +1,5 @@ | |||||
| using Discord.Serialization; | using Discord.Serialization; | ||||
| using Discord.Serialization.Json; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||
| using System.IO; | using System.IO; | ||||
| @@ -16,7 +17,7 @@ namespace Discord.Rest | |||||
| public DiscordRestClient() : this(new DiscordRestConfig()) { } | public DiscordRestClient() : this(new DiscordRestConfig()) { } | ||||
| public DiscordRestClient(DiscordRestConfig config) : base(config) | public DiscordRestClient(DiscordRestConfig config) : base(config) | ||||
| { | { | ||||
| _serializer = new Serializer(SerializationFormat.Json); | |||||
| _serializer = DiscordJsonSerializer.Global.CreateScope(); | |||||
| _serializer.Error += ex => | _serializer.Error += ex => | ||||
| { | { | ||||
| _restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | _restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | ||||
| @@ -1,4 +1,6 @@ | |||||
| using Discord.Serialization; | |||||
| using Discord.Rest; | |||||
| using Discord.Serialization; | |||||
| using Discord.Serialization.Json; | |||||
| using System; | using System; | ||||
| #if DEBUG_LIMITS | #if DEBUG_LIMITS | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| @@ -104,7 +106,7 @@ namespace Discord.Net.Queue | |||||
| { | { | ||||
| try | try | ||||
| { | { | ||||
| var error = Serializer.Json.Read<Error>(response.Data); | |||||
| var error = DiscordJsonSerializer.Global.Read<Error>(response.Data); | |||||
| code = error.Code; | code = error.Code; | ||||
| reason = error.Message; | reason = error.Message; | ||||
| } | } | ||||
| @@ -0,0 +1,23 @@ | |||||
| using Discord.Serialization.Json.Converters; | |||||
| using System; | |||||
| using System.Reflection; | |||||
| namespace Discord.Serialization.Json | |||||
| { | |||||
| internal class DiscordJsonSerializer : JsonSerializer | |||||
| { | |||||
| private static readonly Lazy<DiscordJsonSerializer> _singleton = new Lazy<DiscordJsonSerializer>(); | |||||
| public static new DiscordJsonSerializer Global => _singleton.Value; | |||||
| public DiscordJsonSerializer() | |||||
| { | |||||
| AddConverter<API.Image, ImagePropertyConverter>(); | |||||
| AddConverter<long, Int53PropertyConverter>((type, prop) => prop?.GetCustomAttribute<Int53Attribute>() != null); | |||||
| AddConverter<ulong, UInt53PropertyConverter>((type, prop) => prop?.GetCustomAttribute<Int53Attribute>() != null); | |||||
| AddGenericConverter(typeof(API.EntityOrId<>), typeof(EntityOrIdPropertyConverter<>)); | |||||
| AddGenericConverter(typeof(Optional<>), typeof(OptionalPropertyConverter<>)); | |||||
| } | |||||
| protected DiscordJsonSerializer(JsonSerializer parent) : base(parent) { } | |||||
| public new DiscordJsonSerializer CreateScope() => new DiscordJsonSerializer(this); | |||||
| } | |||||
| } | |||||
| @@ -1,11 +1,12 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Linq; | |||||
| using System.Reflection; | using System.Reflection; | ||||
| namespace Discord.Serialization | namespace Discord.Serialization | ||||
| { | { | ||||
| public class ConverterCollection | |||||
| internal class ConverterCollection | |||||
| { | { | ||||
| private class ConverterTypeCollection | private class ConverterTypeCollection | ||||
| { | { | ||||
| @@ -13,27 +14,30 @@ namespace Discord.Serialization | |||||
| public List<(Func<TypeInfo, PropertyInfo, bool> Condition, Type ConverterType)> Conditionals = new List<(Func<TypeInfo, PropertyInfo, bool>, Type)>(); | public List<(Func<TypeInfo, PropertyInfo, bool> Condition, Type ConverterType)> Conditionals = new List<(Func<TypeInfo, PropertyInfo, bool>, Type)>(); | ||||
| } | } | ||||
| private static readonly MethodInfo _getConverterMethod | |||||
| = typeof(ConverterCollection).GetTypeInfo().GetDeclaredMethod(nameof(Get)); | |||||
| private static readonly TypeInfo _serializerType = typeof(Serializer).GetTypeInfo(); | |||||
| private readonly Serializer _serializer; | |||||
| private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>(); | private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>(); | ||||
| private readonly Dictionary<Type, ConverterTypeCollection> _types = new Dictionary<Type, ConverterTypeCollection>(); | private readonly Dictionary<Type, ConverterTypeCollection> _types = new Dictionary<Type, ConverterTypeCollection>(); | ||||
| private readonly Dictionary<Type, ConverterTypeCollection> _mappedGenericTypes = new Dictionary<Type, ConverterTypeCollection>(); | private readonly Dictionary<Type, ConverterTypeCollection> _mappedGenericTypes = new Dictionary<Type, ConverterTypeCollection>(); | ||||
| private readonly ConverterTypeCollection _genericTypes = new ConverterTypeCollection(); | private readonly ConverterTypeCollection _genericTypes = new ConverterTypeCollection(); | ||||
| internal ConverterCollection() { } | |||||
| internal ConverterCollection(Serializer serializer) | |||||
| { | |||||
| _serializer = serializer; | |||||
| } | |||||
| public void Add<TType, TConverter>() | |||||
| public void Add(Type type, Type converterType) | |||||
| { | { | ||||
| if (!_types.TryGetValue(typeof(TType), out var converters)) | |||||
| _types.Add(typeof(TType), converters = new ConverterTypeCollection()); | |||||
| converters.DefaultConverterType = typeof(TConverter); | |||||
| if (!_types.TryGetValue(type, out var converters)) | |||||
| _types.Add(type, converters = new ConverterTypeCollection()); | |||||
| converters.DefaultConverterType = converterType; | |||||
| } | } | ||||
| public void Add<TType, TConverter>(Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| public void Add(Type type, Type converterType, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| { | { | ||||
| if (!_types.TryGetValue(typeof(TType), out var converters)) | |||||
| _types.Add(typeof(TType), converters = new ConverterTypeCollection()); | |||||
| converters.Conditionals.Add((condition, typeof(TConverter))); | |||||
| if (!_types.TryGetValue(type, out var converters)) | |||||
| _types.Add(type, converters = new ConverterTypeCollection()); | |||||
| converters.Conditionals.Add((condition, converterType)); | |||||
| } | } | ||||
| public void AddGeneric(Type openConverterType) | public void AddGeneric(Type openConverterType) | ||||
| @@ -63,12 +67,12 @@ namespace Discord.Serialization | |||||
| converters.Conditionals.Add((condition, openConverterType)); | converters.Conditionals.Add((condition, openConverterType)); | ||||
| } | } | ||||
| public object Get<TType>(PropertyInfo propInfo = null) | |||||
| public object Get(Type type, PropertyInfo propInfo = null) | |||||
| { | { | ||||
| if (!_cache.TryGetValue(typeof(TType), out var result)) | |||||
| if (!_cache.TryGetValue(type, out var result)) | |||||
| { | { | ||||
| object converter = Create(typeof(TType), propInfo); | |||||
| result = _cache.GetOrAdd(typeof(TType), converter); | |||||
| object converter = Create(type, propInfo); | |||||
| result = _cache.GetOrAdd(type, converter); | |||||
| } | } | ||||
| return result; | return result; | ||||
| } | } | ||||
| @@ -84,7 +88,7 @@ namespace Discord.Serialization | |||||
| { | { | ||||
| var innerType = typeInfo.GenericTypeArguments[0]; | var innerType = typeInfo.GenericTypeArguments[0]; | ||||
| converterType = converterType.MakeGenericType(innerType); | converterType = converterType.MakeGenericType(innerType); | ||||
| object innerConverter = GetInnerConverter(innerType, propInfo); | |||||
| object innerConverter = Get(innerType, propInfo); | |||||
| return Activator.CreateInstance(converterType, innerConverter); | return Activator.CreateInstance(converterType, innerConverter); | ||||
| } | } | ||||
| } | } | ||||
| @@ -102,14 +106,30 @@ namespace Discord.Serialization | |||||
| if (converterType != null) | if (converterType != null) | ||||
| { | { | ||||
| converterType = converterType.MakeGenericType(type); | converterType = converterType.MakeGenericType(type); | ||||
| return Activator.CreateInstance(converterType); | |||||
| var converterTypeInfo = converterType.GetTypeInfo(); | |||||
| var constructors = converterTypeInfo.DeclaredConstructors.ToArray(); | |||||
| if (constructors.Length == 0) | |||||
| throw new SerializationException($"{converterType.Name} is missing a constructor"); | |||||
| if (constructors.Length != 1) | |||||
| throw new SerializationException($"{converterType.Name} has multiple constructors"); | |||||
| var constructor = constructors[0]; | |||||
| var parameters = constructor.GetParameters(); | |||||
| if (parameters.Length == 0) | |||||
| return constructor.Invoke(null); | |||||
| else if (parameters.Length == 1) | |||||
| { | |||||
| var parameterType = parameters[0].ParameterType.GetTypeInfo(); | |||||
| if (_serializerType.IsAssignableFrom(parameterType)) | |||||
| return constructor.Invoke(new object[] { _serializer }); | |||||
| } | |||||
| throw new SerializationException($"{converterType.Name} has an unsupported constructor"); | |||||
| } | } | ||||
| } | } | ||||
| throw new InvalidOperationException($"Unsupported model type: {type.Name}"); | throw new InvalidOperationException($"Unsupported model type: {type.Name}"); | ||||
| } | } | ||||
| private object GetInnerConverter(Type type, PropertyInfo propInfo) | |||||
| => _getConverterMethod.MakeGenericMethod(type).Invoke(this, new object[] { propInfo }); | |||||
| private Type FindConverterType(Type type, Dictionary<Type, ConverterTypeCollection> collection, TypeInfo typeInfo, PropertyInfo propInfo) | private Type FindConverterType(Type type, Dictionary<Type, ConverterTypeCollection> collection, TypeInfo typeInfo, PropertyInfo propInfo) | ||||
| { | { | ||||
| @@ -5,8 +5,13 @@ namespace Discord.Serialization.Json.Converters | |||||
| internal class ObjectPropertyConverter<T> : IJsonPropertyConverter<T> | internal class ObjectPropertyConverter<T> : IJsonPropertyConverter<T> | ||||
| where T : class, new() | where T : class, new() | ||||
| { | { | ||||
| private static readonly ModelMap<T> _map = SerializationFormat.Json.MapModel<T>(); | |||||
| private readonly ModelMap<T> _map; | |||||
| public ObjectPropertyConverter(Serializer serializer) | |||||
| { | |||||
| _map = serializer.MapModel<T>(); | |||||
| } | |||||
| public T Read(PropertyMap map, ref JsonReader reader, bool isTopLevel) | public T Read(PropertyMap map, ref JsonReader reader, bool isTopLevel) | ||||
| { | { | ||||
| var model = new T(); | var model = new T(); | ||||
| @@ -1,5 +1,4 @@ | |||||
| using Discord.Serialization.Json.Converters; | |||||
| using System; | |||||
| using System; | |||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||
| using System.Reflection; | using System.Reflection; | ||||
| using System.Text; | using System.Text; | ||||
| @@ -8,9 +7,12 @@ using System.Text.Json; | |||||
| namespace Discord.Serialization.Json | namespace Discord.Serialization.Json | ||||
| { | { | ||||
| public class JsonFormat : SerializationFormat | |||||
| public class JsonSerializer : Serializer | |||||
| { | { | ||||
| public JsonFormat() | |||||
| private static readonly Lazy<JsonSerializer> _singleton = new Lazy<JsonSerializer>(); | |||||
| public static JsonSerializer Global => _singleton.Value; | |||||
| public JsonSerializer() | |||||
| { | { | ||||
| AddConverter<sbyte, Converters.Int8PropertyConverter>(); | AddConverter<sbyte, Converters.Int8PropertyConverter>(); | ||||
| AddConverter<short, Converters.Int16PropertyConverter>(); | AddConverter<short, Converters.Int16PropertyConverter>(); | ||||
| @@ -41,42 +43,34 @@ namespace Discord.Serialization.Json | |||||
| AddGenericConverter(typeof(Converters.EnumPropertyConverter<>), (type, prop) => type.IsEnum); | AddGenericConverter(typeof(Converters.EnumPropertyConverter<>), (type, prop) => type.IsEnum); | ||||
| AddGenericConverter(typeof(Converters.ObjectPropertyConverter<>), (type, prop) => type.IsClass); | AddGenericConverter(typeof(Converters.ObjectPropertyConverter<>), (type, prop) => type.IsClass); | ||||
| } | } | ||||
| protected JsonSerializer(JsonSerializer parent) : base(parent) { } | |||||
| public JsonSerializer CreateScope() => new JsonSerializer(this); | |||||
| public void AddConverter<TValue, TConverter>() | public void AddConverter<TValue, TConverter>() | ||||
| where TConverter : class, IJsonPropertyConverter<TValue> | where TConverter : class, IJsonPropertyConverter<TValue> | ||||
| => _converters.Add<TValue, TConverter>(); | |||||
| => AddConverter(typeof(TValue), typeof(TConverter)); | |||||
| public void AddConverter<TValue, TConverter>(Func<TypeInfo, PropertyInfo, bool> condition) | public void AddConverter<TValue, TConverter>(Func<TypeInfo, PropertyInfo, bool> condition) | ||||
| where TConverter : class, IJsonPropertyConverter<TValue> | where TConverter : class, IJsonPropertyConverter<TValue> | ||||
| => _converters.Add<TValue, TConverter>(condition); | |||||
| public void AddGenericConverter(Type converter) | |||||
| => _converters.AddGeneric(converter); | |||||
| public void AddGenericConverter(Type converter, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| => _converters.AddGeneric(converter, condition); | |||||
| public void AddGenericConverter(Type value, Type converter) | |||||
| => _converters.AddGeneric(value, converter); | |||||
| public void AddGenericConverter(Type value, Type converter, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| => _converters.AddGeneric(value, converter, condition); | |||||
| => AddConverter(typeof(TValue), typeof(TConverter), condition); | |||||
| protected override PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo) | protected override PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo) | ||||
| { | { | ||||
| var converter = (IJsonPropertyConverter<TValue>)_converters.Get<TValue>(propInfo); | |||||
| var converter = (IJsonPropertyConverter<TValue>)GetConverter(typeof(TValue), propInfo); | |||||
| return new JsonPropertyMap<TModel, TValue>(propInfo, converter); | return new JsonPropertyMap<TModel, TValue>(propInfo, converter); | ||||
| } | } | ||||
| protected internal override TModel Read<TModel>(Serializer serializer, ReadOnlyBuffer<byte> data) | |||||
| public override TModel Read<TModel>(ReadOnlyBuffer<byte> data) | |||||
| { | { | ||||
| var reader = new JsonReader(data.Span, SymbolTable.InvariantUtf8); | var reader = new JsonReader(data.Span, SymbolTable.InvariantUtf8); | ||||
| if (!reader.Read()) | if (!reader.Read()) | ||||
| return null; | return null; | ||||
| var converter = _converters.Get<TModel>() as IJsonPropertyConverter<TModel>; | |||||
| var converter = GetConverter(typeof(TModel)) as IJsonPropertyConverter<TModel>; | |||||
| return converter.Read(null, ref reader, false); | return converter.Read(null, ref reader, false); | ||||
| } | } | ||||
| protected internal override void Write<TModel>(Serializer serializer, ArrayFormatter stream, TModel model) | |||||
| public override void Write<TModel>(ArrayFormatter stream, TModel model) | |||||
| { | { | ||||
| var writer = new JsonWriter(stream); | var writer = new JsonWriter(stream); | ||||
| var converter = _converters.Get<TModel>() as IJsonPropertyConverter<TModel>; | |||||
| var converter = GetConverter(typeof(TModel)) as IJsonPropertyConverter<TModel>; | |||||
| converter.Write(null, ref writer, model, false); | converter.Write(null, ref writer, model, false); | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,55 +0,0 @@ | |||||
| using Discord.Serialization.Json; | |||||
| using System; | |||||
| using System.Collections.Concurrent; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Text.Formatting; | |||||
| namespace Discord.Serialization | |||||
| { | |||||
| public abstract class SerializationFormat | |||||
| { | |||||
| private static readonly MethodInfo _getConverterMethod | |||||
| = typeof(SerializationFormat).GetTypeInfo().GetDeclaredMethod(nameof(CreatePropertyMap)); | |||||
| private static readonly Lazy<JsonFormat> _json = new Lazy<JsonFormat>(() => new JsonFormat()); | |||||
| public static JsonFormat Json => _json.Value; | |||||
| protected readonly ConcurrentDictionary<Type, object> _maps = new ConcurrentDictionary<Type, object>(); | |||||
| protected readonly ConverterCollection _converters = new ConverterCollection(); | |||||
| protected internal ModelMap<TModel> MapModel<TModel>() | |||||
| where TModel : class, new() | |||||
| { | |||||
| return _maps.GetOrAdd(typeof(TModel), _ => | |||||
| { | |||||
| var type = typeof(TModel).GetTypeInfo(); | |||||
| var propInfos = type.DeclaredProperties | |||||
| .Where(x => x.CanRead && x.CanWrite) | |||||
| .ToArray(); | |||||
| var properties = new List<PropertyMap>(); | |||||
| for (int i = 0; i < propInfos.Length; i++) | |||||
| { | |||||
| var propMap = MapProperty<TModel>(propInfos[i]); | |||||
| properties.Add(propMap); | |||||
| } | |||||
| return new ModelMap<TModel>(properties); | |||||
| }) as ModelMap<TModel>; | |||||
| } | |||||
| private PropertyMap MapProperty<TModel>(PropertyInfo propInfo) | |||||
| where TModel : class, new() | |||||
| => _getConverterMethod.MakeGenericMethod(typeof(TModel), propInfo.PropertyType).Invoke(this, new object[] { propInfo }) as PropertyMap; | |||||
| protected internal abstract TModel Read<TModel>(Serializer serializer, ReadOnlyBuffer<byte> data) | |||||
| where TModel : class, new(); | |||||
| protected internal abstract void Write<TModel>(Serializer serializer, ArrayFormatter stream, TModel model) | |||||
| where TModel : class, new(); | |||||
| protected abstract PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo) | |||||
| where TModel : class, new(); | |||||
| } | |||||
| } | |||||
| @@ -1,27 +1,107 @@ | |||||
| using System; | using System; | ||||
| using System.Collections.Concurrent; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Reflection; | |||||
| using System.Text.Formatting; | using System.Text.Formatting; | ||||
| namespace Discord.Serialization | namespace Discord.Serialization | ||||
| { | { | ||||
| public class Serializer | |||||
| public abstract class Serializer | |||||
| { | { | ||||
| private readonly static Lazy<Serializer> _json = new Lazy<Serializer>(() => new Serializer(SerializationFormat.Json)); | |||||
| public static Serializer Json => _json.Value; | |||||
| public event Action<Exception> Error; //TODO: Impl | public event Action<Exception> Error; //TODO: Impl | ||||
| private readonly SerializationFormat _format; | |||||
| private static readonly MethodInfo _createPropertyMapMethod | |||||
| = typeof(Serializer).GetTypeInfo().GetDeclaredMethod(nameof(CreatePropertyMap)); | |||||
| private readonly ConcurrentDictionary<Type, object> _maps; | |||||
| private readonly ConverterCollection _converters; | |||||
| public bool IsScoped { get; } | |||||
| protected Serializer() | |||||
| { | |||||
| _maps = new ConcurrentDictionary<Type, object>(); | |||||
| _converters = new ConverterCollection(this); | |||||
| IsScoped = false; | |||||
| } | |||||
| protected Serializer(Serializer parent) | |||||
| { | |||||
| _maps = parent._maps; | |||||
| _converters = parent._converters; | |||||
| IsScoped = true; | |||||
| } | |||||
| protected object GetConverter(Type type, PropertyInfo propInfo = null) | |||||
| => _converters.Get(type, propInfo); | |||||
| public void AddConverter(Type type, Type converter) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.Add(type, converter); | |||||
| } | |||||
| public void AddConverter(Type type, Type converter, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.Add(type, converter, condition); | |||||
| } | |||||
| public void AddGenericConverter(Type converter) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.AddGeneric(converter); | |||||
| } | |||||
| public void AddGenericConverter(Type converter, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.AddGeneric(converter, condition); | |||||
| } | |||||
| public void AddGenericConverter(Type value, Type converter) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.AddGeneric(value, converter); | |||||
| } | |||||
| public void AddGenericConverter(Type value, Type converter, Func<TypeInfo, PropertyInfo, bool> condition) | |||||
| { | |||||
| CheckScoped(); | |||||
| _converters.AddGeneric(value, converter, condition); | |||||
| } | |||||
| public Serializer(SerializationFormat format) | |||||
| protected internal ModelMap<TModel> MapModel<TModel>() | |||||
| where TModel : class, new() | |||||
| { | { | ||||
| _format = format; | |||||
| return _maps.GetOrAdd(typeof(TModel), _ => | |||||
| { | |||||
| var type = typeof(TModel).GetTypeInfo(); | |||||
| var propInfos = type.DeclaredProperties | |||||
| .Where(x => x.CanRead && x.CanWrite) | |||||
| .ToArray(); | |||||
| var properties = new List<PropertyMap>(); | |||||
| for (int i = 0; i < propInfos.Length; i++) | |||||
| { | |||||
| var propMap = MapProperty<TModel>(propInfos[i]); | |||||
| properties.Add(propMap); | |||||
| } | |||||
| return new ModelMap<TModel>(properties); | |||||
| }) as ModelMap<TModel>; | |||||
| } | } | ||||
| public T Read<T>(ReadOnlyBuffer<byte> data) | |||||
| where T : class, new() | |||||
| => _format.Read<T>(this, data); | |||||
| public void Write<T>(ArrayFormatter data, T obj) | |||||
| where T : class, new() | |||||
| => _format.Write(this, data, obj); | |||||
| private PropertyMap MapProperty<TModel>(PropertyInfo propInfo) | |||||
| where TModel : class, new() | |||||
| => _createPropertyMapMethod.MakeGenericMethod(typeof(TModel), propInfo.PropertyType).Invoke(this, new object[] { propInfo }) as PropertyMap; | |||||
| protected abstract PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo) | |||||
| where TModel : class, new(); | |||||
| public abstract TModel Read<TModel>(ReadOnlyBuffer<byte> data) | |||||
| where TModel : class, new(); | |||||
| public abstract void Write<TModel>(ArrayFormatter stream, TModel model) | |||||
| where TModel : class, new(); | |||||
| private void CheckScoped() | |||||
| { | |||||
| if (IsScoped) | |||||
| throw new InvalidOperationException("Scoped serializers are read-only"); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||