@@ -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"); | |||||
} | |||||
} | } | ||||
} | } |