Browse Source

Fixed several serialization errors, added dictionary and array support

voice-allocs
RogueException 7 years ago
parent
commit
2381de6bd7
45 changed files with 863 additions and 451 deletions
  1. +10
    -2
      src/Discord.Net.Rest/DiscordRestApiClient.cs
  2. +1
    -1
      src/Discord.Net.Rest/DiscordRestClient.cs
  3. +1
    -1
      src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs
  4. +8
    -8
      src/Discord.Net.Rest/Serialization/Json/Converters/EntityOrIdPropertyConverter.cs
  5. +5
    -5
      src/Discord.Net.Rest/Serialization/Json/Converters/ImagePropertyConverter.cs
  6. +5
    -5
      src/Discord.Net.Rest/Serialization/Json/Converters/Int53PropertyConverter.cs
  7. +6
    -6
      src/Discord.Net.Rest/Serialization/Json/Converters/OptionalPropertyConverter.cs
  8. +6
    -6
      src/Discord.Net.Rest/Serialization/Json/Converters/UInt53PropertyConverter.cs
  9. +11
    -6
      src/Discord.Net.Rest/Serialization/Json/DiscordRestJsonSerializer.cs
  10. +7
    -10
      src/Discord.Net.Rpc/DiscordRpcApiClient.cs
  11. +1
    -1
      src/Discord.Net.Rpc/DiscordRpcClient.cs
  12. +21
    -0
      src/Discord.Net.Rpc/Serialization/Json/DiscordRpcJsonSerializer.cs
  13. +138
    -55
      src/Discord.Net.Serialization/ConverterCollection.cs
  14. +0
    -37
      src/Discord.Net.Serialization/Json/Converters/Collections.cs
  15. +46
    -0
      src/Discord.Net.Serialization/Json/Converters/Dictionary.cs
  16. +7
    -7
      src/Discord.Net.Serialization/Json/Converters/Dynamic.cs
  17. +21
    -21
      src/Discord.Net.Serialization/Json/Converters/Enum.cs
  18. +77
    -0
      src/Discord.Net.Serialization/Json/Converters/List.cs
  19. +8
    -8
      src/Discord.Net.Serialization/Json/Converters/Nullable.cs
  20. +26
    -12
      src/Discord.Net.Serialization/Json/Converters/Object.cs
  21. +10
    -10
      src/Discord.Net.Serialization/Json/Converters/Primitives.DateTime.cs
  22. +15
    -15
      src/Discord.Net.Serialization/Json/Converters/Primitives.Float.cs
  23. +10
    -10
      src/Discord.Net.Serialization/Json/Converters/Primitives.Other.cs
  24. +20
    -20
      src/Discord.Net.Serialization/Json/Converters/Primitives.Signed.cs
  25. +11
    -11
      src/Discord.Net.Serialization/Json/Converters/Primitives.String.cs
  26. +20
    -20
      src/Discord.Net.Serialization/Json/Converters/Primitives.Unsigned.cs
  27. +46
    -0
      src/Discord.Net.Serialization/Json/Converters/Struct.cs
  28. +81
    -0
      src/Discord.Net.Serialization/Json/DefaultJsonSerializer.cs
  29. +13
    -2
      src/Discord.Net.Serialization/Json/IJsonPropertyConverter.cs
  30. +8
    -8
      src/Discord.Net.Serialization/Json/JsonPropertyMap.cs
  31. +7
    -64
      src/Discord.Net.Serialization/Json/JsonSerializer.cs
  32. +2
    -2
      src/Discord.Net.Serialization/PropertyMap.cs
  33. +20
    -51
      src/Discord.Net.Serialization/Serializer.cs
  34. +1
    -1
      src/Discord.Net.Serialization/_corefxlab/System.Text.Json/System/Text/Json/JsonWriter.cs
  35. +1
    -1
      src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs
  36. +1
    -1
      src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs
  37. +1
    -1
      src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs
  38. +2
    -2
      src/Discord.Net.WebSocket/Audio/AudioClient.cs
  39. +1
    -1
      src/Discord.Net.WebSocket/DiscordShardedClient.cs
  40. +14
    -6
      src/Discord.Net.WebSocket/DiscordSocketApiClient.cs
  41. +31
    -32
      src/Discord.Net.WebSocket/DiscordSocketClient.cs
  42. +2
    -1
      src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs
  43. +119
    -0
      src/Discord.Net.WebSocket/Serialization/Json/DiscordSocketJsonSerializer.cs
  44. +21
    -0
      src/Discord.Net.WebSocket/Serialization/Json/DiscordVoiceJsonSerializer.cs
  45. +1
    -1
      src/Discord.Net.Webhook/DiscordWebhookClient.cs

+ 10
- 2
src/Discord.Net.Rest/DiscordRestApiClient.cs View File

@@ -198,7 +198,11 @@ namespace Discord.API
var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options);
await SendInternalAsync(method, endpoint, request).ConfigureAwait(false);
}
finally { _formatters.Enqueue(data); }
finally
{
data.Clear();
_formatters.Enqueue(data);
}
}

internal Task SendMultipartAsync(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,
@@ -247,7 +251,11 @@ namespace Discord.API
var request = new JsonRestRequest(RestClient, method, endpoint, SerializeJson(data, payload), options);
return DeserializeJson<TResponse>(await SendInternalAsync(method, endpoint, request).ConfigureAwait(false));
}
finally { _formatters.Enqueue(data); }
finally
{
data.Clear();
_formatters.Enqueue(data);
}
}

internal Task<TResponse> SendMultipartAsync<TResponse>(string method, Expression<Func<string>> endpointExpr, IReadOnlyDictionary<string, object> multipartArgs, BucketIds ids,


+ 1
- 1
src/Discord.Net.Rest/DiscordRestClient.cs View File

@@ -17,7 +17,7 @@ namespace Discord.Rest
public DiscordRestClient() : this(new DiscordRestConfig()) { }
public DiscordRestClient(DiscordRestConfig config) : base(config)
{
_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordRestJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();


+ 1
- 1
src/Discord.Net.Rest/Net/Queue/RequestQueueBucket.cs View File

@@ -106,7 +106,7 @@ namespace Discord.Net.Queue
{
try
{
var error = DiscordJsonSerializer.Global.Read<Error>(response.Data);
var error = DiscordRestJsonSerializer.Global.Read<Error>(response.Data);
code = error.Code;
reason = error.Message;
}


+ 8
- 8
src/Discord.Net.Rest/Serialization/Json/Converters/EntityOrIdPropertyConverter.cs View File

@@ -3,16 +3,16 @@ using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class EntityOrIdPropertyConverter<T> : IJsonPropertyConverter<EntityOrId<T>>
internal class EntityOrIdPropertyConverter<T> : JsonPropertyConverter<EntityOrId<T>>
{
private readonly IJsonPropertyConverter<T> _innerConverter;
private readonly JsonPropertyConverter<T> _innerConverter;

public EntityOrIdPropertyConverter(IJsonPropertyConverter<T> innerConverter)
public EntityOrIdPropertyConverter(JsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public EntityOrId<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override EntityOrId<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -21,14 +21,14 @@ namespace Discord.Serialization.Json.Converters
return new EntityOrId<T>(_innerConverter.Read(map, model, ref reader, false));
}

public void Write(PropertyMap map, object model, ref JsonWriter writer, EntityOrId<T> value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, EntityOrId<T> value, string key)
{
if (value.Object != null)
_innerConverter.Write(map, model, ref writer, value.Object, isTopLevel);
_innerConverter.Write(map, model, ref writer, value.Object, key);
else
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.Id);
if (key != null)
writer.WriteAttribute(key, value.Id);
else
writer.WriteValue(value.Id);
}


+ 5
- 5
src/Discord.Net.Rest/Serialization/Json/Converters/ImagePropertyConverter.cs View File

@@ -3,9 +3,9 @@ using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
internal class ImagePropertyConverter : IJsonPropertyConverter<API.Image>
internal class ImagePropertyConverter : JsonPropertyConverter<API.Image>
{
public API.Image Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override API.Image Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -13,7 +13,7 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return new API.Image(reader.ParseString());
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, API.Image value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, API.Image value, string key)
{
string str;
if (value.Stream != null)
@@ -43,8 +43,8 @@ namespace Discord.Serialization.Json.Converters
else
str = value.Hash;

if (isTopLevel)
writer.WriteAttribute(map.Key, str);
if (key != null)
writer.WriteAttribute(key, str);
else
writer.WriteValue(str);
}


+ 5
- 5
src/Discord.Net.Rest/Serialization/Json/Converters/Int53PropertyConverter.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
internal class Int53PropertyConverter : IJsonPropertyConverter<long>
internal class Int53PropertyConverter : JsonPropertyConverter<long>
{
public long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,10 +12,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number");
return reader.ParseInt64();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, long value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, long value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value.ToString());
}


+ 6
- 6
src/Discord.Net.Rest/Serialization/Json/Converters/OptionalPropertyConverter.cs View File

@@ -2,22 +2,22 @@

namespace Discord.Serialization.Json.Converters
{
internal class OptionalPropertyConverter<T> : IJsonPropertyConverter<Optional<T>>
internal class OptionalPropertyConverter<T> : JsonPropertyConverter<Optional<T>>
{
private readonly IJsonPropertyConverter<T> _innerConverter;
private readonly JsonPropertyConverter<T> _innerConverter;

public OptionalPropertyConverter(IJsonPropertyConverter<T> innerConverter)
public OptionalPropertyConverter(JsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public Optional<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override Optional<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
=> new Optional<T>(_innerConverter.Read(map, model, ref reader, isTopLevel));

public void Write(PropertyMap map, object model, ref JsonWriter writer, Optional<T> value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, Optional<T> value, string key)
{
if (value.IsSpecified)
_innerConverter.Write(map, model, ref writer, value.Value, isTopLevel);
_innerConverter.Write(map, model, ref writer, value.Value, key);
}
}
}

+ 6
- 6
src/Discord.Net.Rest/Serialization/Json/Converters/UInt53PropertyConverter.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
internal class UInt53PropertyConverter : IJsonPropertyConverter<ulong>
internal class UInt53PropertyConverter : JsonPropertyConverter<ulong>
{
public ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,12 +12,12 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number");
return reader.ParseUInt64();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value.ToString());
writer.WriteValue(value);
}
}
}

src/Discord.Net.Rest/Serialization/Json/DiscordJsonSerializer.cs → src/Discord.Net.Rest/Serialization/Json/DiscordRestJsonSerializer.cs View File

@@ -4,12 +4,15 @@ using System.Reflection;

namespace Discord.Serialization.Json
{
public class DiscordJsonSerializer : JsonSerializer
public class DiscordRestJsonSerializer : JsonSerializer
{
private static readonly Lazy<DiscordJsonSerializer> _singleton = new Lazy<DiscordJsonSerializer>();
public static new DiscordJsonSerializer Global => _singleton.Value;
private static readonly Lazy<DiscordRestJsonSerializer> _singleton = new Lazy<DiscordRestJsonSerializer>();
public static DiscordRestJsonSerializer Global => _singleton.Value;

public DiscordJsonSerializer()
public DiscordRestJsonSerializer()
: this((JsonSerializer)null) { }
public DiscordRestJsonSerializer(JsonSerializer parent)
: base(parent ?? DefaultJsonSerializer.Global)
{
AddConverter<API.Image, ImagePropertyConverter>();
AddConverter<long, Int53PropertyConverter>((type, prop) => prop?.GetCustomAttribute<Int53Attribute>() != null);
@@ -18,7 +21,9 @@ namespace Discord.Serialization.Json
AddGenericConverter(typeof(API.EntityOrId<>), typeof(EntityOrIdPropertyConverter<>));
AddGenericConverter(typeof(Optional<>), typeof(OptionalPropertyConverter<>));
}
protected DiscordJsonSerializer(JsonSerializer parent) : base(parent) { }
public new DiscordJsonSerializer CreateScope() => new DiscordJsonSerializer(this);

private DiscordRestJsonSerializer(DiscordRestJsonSerializer parent)
: base(parent) { }
public DiscordRestJsonSerializer CreateScope() => new DiscordRestJsonSerializer(this);
}
}

+ 7
- 10
src/Discord.Net.Rpc/DiscordRpcApiClient.cs View File

@@ -42,11 +42,11 @@ namespace Discord.API
}
public Task SetResultAsync(ReadOnlyBuffer<byte> data)
{
return Promise.TrySetResultAsync(DiscordJsonSerializer.Global.Read<T>(data));
return Promise.TrySetResultAsync(DiscordRestJsonSerializer.Global.Read<T>(data));
}
public Task SetExceptionAsync(ReadOnlyBuffer<byte> data)
{
var error = DiscordJsonSerializer.Global.Read<ErrorEvent>(data);
var error = DiscordRestJsonSerializer.Global.Read<ErrorEvent>(data);
return Promise.TrySetExceptionAsync(new RpcException(error.Code, error.Message));
}
}
@@ -225,26 +225,23 @@ namespace Discord.API
private async Task<TResponse> SendRpcAsyncInternal<TResponse>(string cmd, object payload, Optional<string> evt, RequestOptions options)
where TResponse : class, new()
{
if (_formatters.TryDequeue(out var data1))
data1 = new ArrayFormatter(128, SymbolTable.InvariantUtf8);
if (_formatters.TryDequeue(out var data2))
data2 = new ArrayFormatter(128, SymbolTable.InvariantUtf8);
if (_formatters.TryDequeue(out var data))
data = new ArrayFormatter(128, SymbolTable.InvariantUtf8);
try
{
var guid = Guid.NewGuid();
var frame = new API.Rpc.RpcFrame { Cmd = cmd, Event = evt, Args = SerializeJson(data1, payload), Nonce = guid };
var frame = new API.Rpc.RpcFrame { Cmd = cmd, Event = evt, Args = payload, Nonce = guid };

var requestTracker = new RpcRequest<TResponse>(options);
_requests[guid] = requestTracker;

await RequestQueue.SendAsync(new WebSocketRequest(_webSocketClient, null, SerializeJson(data2, frame), true, options)).ConfigureAwait(false);
await RequestQueue.SendAsync(new WebSocketRequest(_webSocketClient, null, SerializeJson(data, frame), true, options)).ConfigureAwait(false);
await _sentRpcMessageEvent.InvokeAsync(cmd).ConfigureAwait(false);
return await requestTracker.Promise.Task.ConfigureAwait(false);
}
finally
{
_formatters.Enqueue(data1);
_formatters.Enqueue(data2);
_formatters.Enqueue(data);
}
}



+ 1
- 1
src/Discord.Net.Rpc/DiscordRpcClient.cs View File

@@ -38,7 +38,7 @@ namespace Discord.Rpc
_authorizeLock = new SemaphoreSlim(1, 1);
_rpcLogger = LogManager.CreateLogger("RPC");

_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordRpcJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_rpcLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();


+ 21
- 0
src/Discord.Net.Rpc/Serialization/Json/DiscordRpcJsonSerializer.cs View File

@@ -0,0 +1,21 @@
using System;

namespace Discord.Serialization.Json
{
public class DiscordRpcJsonSerializer : JsonSerializer
{
private static readonly Lazy<DiscordRpcJsonSerializer> _singleton = new Lazy<DiscordRpcJsonSerializer>();
public static DiscordRpcJsonSerializer Global => _singleton.Value;

public DiscordRpcJsonSerializer()
: this((JsonSerializer)null) { }
public DiscordRpcJsonSerializer(JsonSerializer parent)
: base(parent ?? DiscordRestJsonSerializer.Global)
{
}

private DiscordRpcJsonSerializer(DiscordRpcJsonSerializer parent)
: base(parent) { }
public DiscordRpcJsonSerializer CreateScope() => new DiscordRpcJsonSerializer(this);
}
}

+ 138
- 55
src/Discord.Net.Serialization/ConverterCollection.cs View File

@@ -8,28 +8,46 @@ namespace Discord.Serialization
{
internal class ConverterCollection
{
private class GenericConverterType
{
public Type Type;
public Func<Type, Type> InnerTypeSelector;

public GenericConverterType(Type type, Func<Type, Type> innerTypeSelector)
{
Type = type;
InnerTypeSelector = innerTypeSelector ?? (x => x);
}
}
private class ConverterTypeCollection
{
public Type DefaultConverterType;
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 class GenericConverterTypeCollection
{
public GenericConverterType DefaultConverterType;
public List<(Func<TypeInfo, PropertyInfo, bool> Condition, GenericConverterType ConverterType)> Conditionals
= new List<(Func<TypeInfo, PropertyInfo, bool>, GenericConverterType)>();
}

private static readonly TypeInfo _serializerType = typeof(Serializer).GetTypeInfo();

private readonly Serializer _serializer;
private readonly ConverterCollection _parent;
private readonly ConcurrentDictionary<Type, object> _cache;
private readonly Dictionary<Type, ConverterTypeCollection> _types;
private readonly Dictionary<Type, ConverterTypeCollection> _mappedGenericTypes;
private readonly ConverterTypeCollection _genericTypes;
private readonly Dictionary<Type, GenericConverterTypeCollection> _mappedGenericTypes;
private readonly GenericConverterTypeCollection _genericTypes;
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, ISelectorGroup>> _selectorGroups;

internal ConverterCollection(Serializer serializer)
internal ConverterCollection(ConverterCollection parent = null)
{
_serializer = serializer;
_parent = parent;
_cache = new ConcurrentDictionary<Type, object>();
_types = new Dictionary<Type, ConverterTypeCollection>();
_mappedGenericTypes = new Dictionary<Type, ConverterTypeCollection>();
_genericTypes = new ConverterTypeCollection();
_mappedGenericTypes = new Dictionary<Type, GenericConverterTypeCollection>();
_genericTypes = new GenericConverterTypeCollection();
_selectorGroups = new ConcurrentDictionary<Type, ConcurrentDictionary<string, ISelectorGroup>>();
}

@@ -46,63 +64,80 @@ namespace Discord.Serialization
converters.Conditionals.Add((condition, converterType));
}

public void AddGeneric(Type openConverterType)
public void AddGeneric(Type openConverterType, Func<Type, Type> innerTypeSelector = null)
{
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
_genericTypes.DefaultConverterType = openConverterType;
_genericTypes.DefaultConverterType = new GenericConverterType(openConverterType, innerTypeSelector);
}
public void AddGeneric(Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition)
public void AddGeneric(Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition, Func<Type, Type> innerTypeSelector = null)
{
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
_genericTypes.Conditionals.Add((condition, openConverterType));
_genericTypes.Conditionals.Add((condition, new GenericConverterType(openConverterType, innerTypeSelector)));
}
public void AddGeneric(Type openType, Type openConverterType)
public void AddGeneric(Type openType, Type openConverterType, Func<Type, Type> innerTypeSelector = null)
{
if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
if (!_mappedGenericTypes.TryGetValue(openType, out var converters))
_mappedGenericTypes.Add(openType, converters = new ConverterTypeCollection());
converters.DefaultConverterType = openConverterType;
_mappedGenericTypes.Add(openType, converters = new GenericConverterTypeCollection());
converters.DefaultConverterType = new GenericConverterType(openConverterType, innerTypeSelector);
}
public void AddGeneric(Type openType, Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition)
public void AddGeneric(Type openType, Type openConverterType, Func<TypeInfo, PropertyInfo, bool> condition, Func<Type, Type> innerTypeSelector = null)
{
if (openType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openType)} must be an open generic");
if (openConverterType.IsConstructedGenericType) throw new InvalidOperationException($"{nameof(openConverterType)} must be an open generic");
if (!_mappedGenericTypes.TryGetValue(openType, out var converters))
_mappedGenericTypes.Add(openType, converters = new ConverterTypeCollection());
converters.Conditionals.Add((condition, openConverterType));
_mappedGenericTypes.Add(openType, converters = new GenericConverterTypeCollection());
converters.Conditionals.Add((condition, new GenericConverterType(openConverterType, innerTypeSelector)));
}

public void AddSelector(string groupKey, Type keyType, object keyValue, Type converterType)
public void AddSelector(Serializer serializer, string groupKey, Type keyType, object keyValue, Type converterType)
{
var group = GetSelectorGroup(keyType, groupKey);
group.AddDynamicConverter(keyValue, converterType);
var group = CreateSelectorGroup(keyType, groupKey);
group.AddDynamicConverter(keyValue, BuildConverter(converterType, serializer));
}

public object Get(Type type, PropertyInfo propInfo = null)
public object Get(Serializer serializer, Type type, PropertyInfo propInfo = null, bool throwOnNotFound = true)
{
//Check parent
object converter = _parent?.Get(serializer, type, propInfo, false);
if (converter != null)
return converter;

if (propInfo == null) //Can only cache top-level due to attribute influences
{
if (_cache.TryGetValue(type, out var result))
return result;
return _cache.GetOrAdd(type, Create(type, propInfo));
converter = Create(serializer, type, propInfo, throwOnNotFound);
if (converter != null)
return _cache.GetOrAdd(type, converter);
return null;
}
return Create(type, propInfo);
return Create(serializer, type, propInfo, throwOnNotFound);
}
private object Create(Type type, PropertyInfo propInfo)
private object Create(Serializer serializer, Type type, PropertyInfo propInfo, bool throwOnNotFound)
{
TypeInfo typeInfo = type.GetTypeInfo();

//Mapped generic converters (List<T> -> CollectionPropertyConverter<T>)
if (typeInfo.IsGenericType)
{
var converterType = FindConverterType(typeInfo.GetGenericTypeDefinition(), _mappedGenericTypes, typeInfo, propInfo);
if (converterType != null)
var openGenericType = typeInfo.GetGenericTypeDefinition();

Type innerType = null;
if (openGenericType == typeof(Dictionary<,>) || openGenericType == typeof(IReadOnlyDictionary<,>)) //TODO: We can only assume key type for JSON
innerType = typeInfo.GenericTypeArguments[1]; //TValue
else if (openGenericType.GetTypeInfo().GenericTypeParameters.Length == 1)
innerType = typeInfo.GenericTypeArguments[0];

if (innerType != null)
{
var innerType = typeInfo.GenericTypeArguments[0];
converterType = converterType.MakeGenericType(innerType);
object innerConverter = Get(innerType, propInfo);
return Activator.CreateInstance(converterType, innerConverter);
var converterType = FindGenericConverterType(openGenericType, innerType, _mappedGenericTypes, typeInfo, propInfo);
if (converterType != null)
{
object innerConverter = serializer.GetConverter(innerType, propInfo);
return BuildConverter(converterType, serializer, innerConverter);
}
}
}

@@ -110,38 +145,52 @@ namespace Discord.Serialization
{
var converterType = FindConverterType(type, _types, typeInfo, propInfo);
if (converterType != null)
return Activator.CreateInstance(converterType);
return BuildConverter(converterType, serializer);
}
//Generic converters (Model -> ObjectPropertyConverter<Model>)
{
var converterType = FindConverterType(_genericTypes, typeInfo, propInfo);
var converterType = FindGenericConverterType(type, _genericTypes, typeInfo, propInfo);
if (converterType != null)
{
converterType = converterType.MakeGenericType(type);
var converterTypeInfo = converterType.GetTypeInfo();

var constructors = converterTypeInfo.DeclaredConstructors.Where(x => !x.IsStatic).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)
if (type.IsArray) //We cant feed arrays through the mapped generic logic, emulate here
{
var parameterType = parameters[0].ParameterType.GetTypeInfo();
if (_serializerType.IsAssignableFrom(parameterType))
return constructor.Invoke(new object[] { _serializer });
object innerConverter = serializer.GetConverter(type.GetElementType(), propInfo);
return BuildConverter(converterType, serializer, innerConverter);
}
throw new SerializationException($"{converterType.Name} has an unsupported constructor");
else
return BuildConverter(converterType, serializer);
}
}

throw new InvalidOperationException($"Unsupported model type: {type.Name}");
if (throwOnNotFound)
throw new InvalidOperationException($"Unsupported model type: {type.Name}");
return null;
}
private object BuildConverter(Type converterType, Serializer serializer, object innerConverter = null)
{
var converterTypeInfo = converterType.GetTypeInfo();

var constructors = converterTypeInfo.DeclaredConstructors.Where(x => !x.IsStatic).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();

var args = new object[parameters.Length];
for (int i = 0; i < args.Length; i++)
{
var paramType = parameters[i].ParameterType;
if (i == args.Length - 1 && innerConverter != null)
args[i] = innerConverter;
else if (_serializerType.IsAssignableFrom(paramType.GetTypeInfo()))
args[i] = serializer;
else
throw new SerializationException($"{converterType.Name} has an unsupported constructor");
}
return constructor.Invoke(args);
}

private Type FindConverterType(Type type, Dictionary<Type, ConverterTypeCollection> collection, TypeInfo typeInfo, PropertyInfo propInfo)
@@ -150,6 +199,12 @@ namespace Discord.Serialization
return FindConverterType(converters, typeInfo, propInfo);
return null;
}
private Type FindGenericConverterType(Type type, Type innerType, Dictionary<Type, GenericConverterTypeCollection> collection, TypeInfo typeInfo, PropertyInfo propInfo)
{
if (collection.TryGetValue(type, out var converters))
return FindGenericConverterType(innerType, converters, typeInfo, propInfo);
return null;
}
private Type FindConverterType(ConverterTypeCollection converters, TypeInfo typeInfo, PropertyInfo propInfo)
{
for (int i = 0; i < converters.Conditionals.Count; i++)
@@ -161,17 +216,45 @@ namespace Discord.Serialization
return converters.DefaultConverterType;
return null;
}
private Type FindGenericConverterType(Type innerType, GenericConverterTypeCollection converters, TypeInfo typeInfo, PropertyInfo propInfo)
{
for (int i = 0; i < converters.Conditionals.Count; i++)
{
if (converters.Conditionals[i].Condition(typeInfo, propInfo))
{
var converterType = converters.Conditionals[i].ConverterType;
return converterType.Type.MakeGenericType(converterType.InnerTypeSelector(innerType));
}
}
if (converters.DefaultConverterType != null)
{
var converterType = converters.DefaultConverterType;
return converterType.Type.MakeGenericType(converterType.InnerTypeSelector(innerType));
}
return null;
}

public ISelectorGroup GetSelectorGroup(Type keyType, string groupKey)
{
var keyGroup = GetSelectorKeyGroup(keyType);
var selectorGroup = _parent?.GetSelectorGroup(keyType, groupKey);
if (selectorGroup != null)
return selectorGroup;

if (_selectorGroups.TryGetValue(keyType, out var keyGroup) &&
keyGroup.TryGetValue(groupKey, out selectorGroup))
return selectorGroup;
return null;
}
public ISelectorGroup CreateSelectorGroup(Type keyType, string groupKey)
{
var keyGroup = CreateSelectorKeyGroup(keyType);
if (keyGroup.TryGetValue(groupKey, out var selectorGroup))
return selectorGroup;
return CreateSelectorGroup(keyType, keyGroup, groupKey);
}
private ISelectorGroup CreateSelectorGroup(Type keyType, ConcurrentDictionary<string, ISelectorGroup> keyGroup, string groupKey)
=> keyGroup.GetOrAdd(groupKey, Activator.CreateInstance(typeof(SelectorGroup<>).MakeGenericType(keyType)) as ISelectorGroup);
private ConcurrentDictionary<string, ISelectorGroup> GetSelectorKeyGroup(Type keyType)
private ConcurrentDictionary<string, ISelectorGroup> CreateSelectorKeyGroup(Type keyType)
=> _selectorGroups.GetOrAdd(keyType, _ => new ConcurrentDictionary<string, ISelectorGroup>());
}
}

+ 0
- 37
src/Discord.Net.Serialization/Json/Converters/Collections.cs View File

@@ -1,37 +0,0 @@
using System.Collections.Generic;
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
public class ListPropertyConverter<T> : IJsonPropertyConverter<List<T>>
{
private readonly IJsonPropertyConverter<T> _innerConverter;

public ListPropertyConverter(IJsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public List<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray)
throw new SerializationException("Bad input, expected StartArray");

var list = new List<T>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
list.Add(_innerConverter.Read(map, model, ref reader, false));
return list;
}

public void Write(PropertyMap map, object model, ref JsonWriter writer, List<T> value, bool isTopLevel)
{
if (isTopLevel)
writer.WriteArrayStart(map.Key);
else
writer.WriteArrayStart();
for (int i = 0; i < value.Count; i++)
_innerConverter.Write(map, model, ref writer, value[i], false);
writer.WriteArrayEnd();
}
}
}

+ 46
- 0
src/Discord.Net.Serialization/Json/Converters/Dictionary.cs View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
public class DictionaryPropertyConverter<TValue> : JsonPropertyConverter<Dictionary<string, TValue>>
{
private readonly JsonPropertyConverter<TValue> _valueConverter;

public DictionaryPropertyConverter(JsonPropertyConverter<TValue> valueConverter)
{
_valueConverter = valueConverter;
}

public override Dictionary<string, TValue> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartObject)
throw new SerializationException("Bad input, expected StartObject");

var dic = new Dictionary<string, TValue>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
return dic;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new SerializationException("Bad input, expected PropertyName");

string key = reader.Value.ParseString();
var value = _valueConverter.Read(map, model, ref reader, false);
dic.Add(key, value);
}
return dic;
}

public override void Write(PropertyMap map, object model, ref JsonWriter writer, Dictionary<string, TValue> value, string key)
{
if (key != null)
writer.WriteObjectStart(key);
else
writer.WriteObjectStart();
foreach (var pair in value)
_valueConverter.Write(map, model, ref writer, pair.Value, pair.Key);
writer.WriteObjectEnd();
}
}
}

+ 7
- 7
src/Discord.Net.Serialization/Json/Converters/Dynamic.cs View File

@@ -3,9 +3,9 @@
namespace Discord.Serialization.Json.Converters
{
//TODO: Only supports cases where the key arrives first
public class DynamicPropertyConverter : IJsonPropertyConverter<object>
public class DynamicPropertyConverter : JsonPropertyConverter<object>
{
public object Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override object Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (map.GetDynamicConverter(model, false) is IJsonPropertyReader<object> converter)
return converter.Read(map, model, ref reader, isTopLevel);
@@ -16,19 +16,19 @@ namespace Discord.Serialization.Json.Converters
}
}

public void Write(PropertyMap map, object model, ref JsonWriter writer, object value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, object value, string key)
{
if (value == null)
{
if (isTopLevel)
writer.WriteAttributeNull(map.Key);
if (key != null)
writer.WriteAttributeNull(key);
else
writer.WriteNull();
}
else
{
var converter = map.GetDynamicConverter(model, true) as IJsonPropertyWriter<object>;
converter.Write(map, model, ref writer, value, isTopLevel);
var converter = (IJsonPropertyWriter)map.GetDynamicConverter(model, true);
converter.Write(map, model, ref writer, value, key);
}
}
}


+ 21
- 21
src/Discord.Net.Serialization/Json/Converters/Enum.cs View File

@@ -2,12 +2,12 @@

namespace Discord.Serialization.Json.Converters
{
public class Int64EnumPropertyConverter<T> : IJsonPropertyConverter<T>
public class Int64EnumPropertyConverter<T> : JsonPropertyConverter<T>
where T : struct
{
private static readonly EnumMap<T> _map = EnumMap.For<T>();

public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -15,22 +15,22 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return _map.FromInt64(reader.Value);
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key)
{
long key = _map.ToInt64(value);
if (isTopLevel)
writer.WriteAttribute(map.Key, key);
long intVal = _map.ToInt64(value);
if (key != null)
writer.WriteAttribute(key, intVal);
else
writer.WriteValue(key);
writer.WriteValue(intVal);
}
}

public class UInt64EnumPropertyConverter<T> : IJsonPropertyConverter<T>
public class UInt64EnumPropertyConverter<T> : JsonPropertyConverter<T>
where T : struct
{
private static readonly EnumMap<T> _map = EnumMap.For<T>();

public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -38,22 +38,22 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return _map.FromUInt64(reader.Value);
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key)
{
ulong key = _map.ToUInt64(value);
if (isTopLevel)
writer.WriteAttribute(map.Key, key);
ulong uintVal = _map.ToUInt64(value);
if (key != null)
writer.WriteAttribute(key, uintVal);
else
writer.WriteValue(key);
writer.WriteValue(uintVal);
}
}

public class StringEnumPropertyConverter<T> : IJsonPropertyConverter<T>
public class StringEnumPropertyConverter<T> : JsonPropertyConverter<T>
where T : struct
{
private static readonly EnumMap<T> _map = EnumMap.For<T>();

public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -61,13 +61,13 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return _map.FromKey(reader.Value);
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key)
{
string key = _map.ToUtf16Key(value);
if (isTopLevel)
writer.WriteAttribute(map.Key, key);
string strVal = _map.ToUtf16Key(value);
if (key != null)
writer.WriteAttribute(key, strVal);
else
writer.WriteValue(key);
writer.WriteValue(strVal);
}
}
}

+ 77
- 0
src/Discord.Net.Serialization/Json/Converters/List.cs View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
public class ArrayPropertyConverter<T> : JsonPropertyConverter<T[]>
{
private readonly JsonPropertyConverter<T> _innerConverter;

public ArrayPropertyConverter(JsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public override T[] Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray)
throw new SerializationException("Bad input, expected StartArray");

var list = new List<T>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
return list.ToArray();
list.Add(_innerConverter.Read(map, model, ref reader, false));
}
throw new SerializationException("Bad input, expected EndArray");
}

public override void Write(PropertyMap map, object model, ref JsonWriter writer, T[] value, string key)
{
if (key != null)
writer.WriteArrayStart(key);
else
writer.WriteArrayStart();
for (int i = 0; i < value.Length; i++)
_innerConverter.Write(map, model, ref writer, value[i], null);
writer.WriteArrayEnd();
}
}

public class ListPropertyConverter<T> : JsonPropertyConverter<List<T>>
{
private readonly JsonPropertyConverter<T> _innerConverter;

public ListPropertyConverter(JsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public override List<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray)
throw new SerializationException("Bad input, expected StartArray");

var list = new List<T>();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndArray)
return list;
list.Add(_innerConverter.Read(map, model, ref reader, false));
}
throw new SerializationException("Bad input, expected EndArray");
}

public override void Write(PropertyMap map, object model, ref JsonWriter writer, List<T> value, string key)
{
if (key != null)
writer.WriteArrayStart(key);
else
writer.WriteArrayStart();
for (int i = 0; i < value.Count; i++)
_innerConverter.Write(map, model, ref writer, value[i], null);
writer.WriteArrayEnd();
}
}
}

+ 8
- 8
src/Discord.Net.Serialization/Json/Converters/Nullable.cs View File

@@ -2,17 +2,17 @@

namespace Discord.Serialization.Json.Converters
{
public class NullablePropertyConverter<T> : IJsonPropertyConverter<T?>
public class NullablePropertyConverter<T> : JsonPropertyConverter<T?>
where T : struct
{
private readonly IJsonPropertyConverter<T> _innerConverter;
private readonly JsonPropertyConverter<T> _innerConverter;

public NullablePropertyConverter(IJsonPropertyConverter<T> innerConverter)
public NullablePropertyConverter(JsonPropertyConverter<T> innerConverter)
{
_innerConverter = innerConverter;
}

public T? Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override T? Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -21,14 +21,14 @@ namespace Discord.Serialization.Json.Converters
return _innerConverter.Read(map, model, ref reader, false);
}

public void Write(PropertyMap map, object model, ref JsonWriter writer, T? value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, T? value, string key)
{
if (value.HasValue)
_innerConverter.Write(map, model, ref writer, value.Value, isTopLevel);
_innerConverter.Write(map, model, ref writer, value.Value, key);
else
{
if (isTopLevel)
writer.WriteAttributeNull(map.Key);
if (key != null)
writer.WriteAttributeNull(key);
else
writer.WriteNull();
}


+ 26
- 12
src/Discord.Net.Serialization/Json/Converters/Object.cs View File

@@ -2,7 +2,7 @@

namespace Discord.Serialization.Json.Converters
{
public class ObjectPropertyConverter<T> : IJsonPropertyConverter<T>
public class ObjectPropertyConverter<T> : JsonPropertyConverter<T>
where T : class, new()
{
private readonly ModelMap<T> _map;
@@ -12,19 +12,23 @@ namespace Discord.Serialization.Json.Converters
_map = serializer.MapModel<T>();
}

public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
var subModel = new T();

if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartObject)
throw new SerializationException("Bad input, expected StartObject");
if ((isTopLevel && !reader.Read()) || (reader.TokenType != JsonTokenType.StartObject && reader.ValueType != JsonValueType.Null))
throw new SerializationException("Bad input, expected StartObject or Null");

if (reader.ValueType == JsonValueType.Null)
return null;

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
return subModel;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new SerializationException("Bad input, expected PropertyName");
if (_map.TryGetProperty(reader.Value, out var property))
(property as IJsonPropertyMap<T>).Read(subModel, ref reader);
else
@@ -32,15 +36,25 @@ namespace Discord.Serialization.Json.Converters
}
throw new SerializationException("Bad input, expected EndObject");
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key)
{
if (isTopLevel)
writer.WriteObjectStart(map.Key);
if (value == null)
{
if (key != null)
writer.WriteAttributeNull(key);
else
writer.WriteNull();
}
else
writer.WriteObjectStart();
for (int i = 0; i < _map.Properties.Length; i++)
(_map.Properties[i] as IJsonPropertyMap<T>).Write(value, ref writer);
writer.WriteObjectEnd();
{
if (key != null)
writer.WriteObjectStart(key);
else
writer.WriteObjectStart();
for (int i = 0; i < _map.Properties.Length; i++)
(_map.Properties[i] as IJsonPropertyMap<T>).Write(value, ref writer);
writer.WriteObjectEnd();
}
}
}
}

+ 10
- 10
src/Discord.Net.Serialization/Json/Converters/Primitives.DateTime.cs View File

@@ -3,9 +3,9 @@ using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
public class DateTimePropertyConverter : IJsonPropertyConverter<DateTime>
public class DateTimePropertyConverter : JsonPropertyConverter<DateTime>
{
public DateTime Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override DateTime Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -13,18 +13,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return reader.ParseDateTime();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, DateTime value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, DateTime value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class DateTimeOffsetPropertyConverter : IJsonPropertyConverter<DateTimeOffset>
public class DateTimeOffsetPropertyConverter : JsonPropertyConverter<DateTimeOffset>
{
public DateTimeOffset Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override DateTimeOffset Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -32,10 +32,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return reader.ParseDateTimeOffset();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, DateTimeOffset value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, DateTimeOffset value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}


+ 15
- 15
src/Discord.Net.Serialization/Json/Converters/Primitives.Float.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
public class SinglePropertyConverter : IJsonPropertyConverter<float>
public class SinglePropertyConverter : JsonPropertyConverter<float>
{
public float Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override float Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,18 +12,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseSingle();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, float value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, float value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}
}

public class DoublePropertyConverter : IJsonPropertyConverter<double>
public class DoublePropertyConverter : JsonPropertyConverter<double>
{
public double Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override double Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -31,18 +31,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseDouble();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, double value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, double value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}
}

internal class DecimalPropertyConverter : IJsonPropertyConverter<decimal>
internal class DecimalPropertyConverter : JsonPropertyConverter<decimal>
{
public decimal Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override decimal Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -50,10 +50,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseDecimal();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, decimal value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, decimal value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}


+ 10
- 10
src/Discord.Net.Serialization/Json/Converters/Primitives.Other.cs View File

@@ -3,9 +3,9 @@ using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
public class BooleanPropertyConverter : IJsonPropertyConverter<bool>
public class BooleanPropertyConverter : JsonPropertyConverter<bool>
{
public bool Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override bool Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -16,18 +16,18 @@ namespace Discord.Serialization.Json.Converters
default: throw new SerializationException("Bad input, expected False or True");
}
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, bool value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, bool value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class GuidPropertyConverter : IJsonPropertyConverter<Guid>
public class GuidPropertyConverter : JsonPropertyConverter<Guid>
{
public Guid Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override Guid Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -35,10 +35,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return reader.ParseGuid();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, Guid value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, Guid value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}


+ 20
- 20
src/Discord.Net.Serialization/Json/Converters/Primitives.Signed.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
public class Int8PropertyConverter : IJsonPropertyConverter<sbyte>
public class Int8PropertyConverter : JsonPropertyConverter<sbyte>
{
public sbyte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override sbyte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,18 +12,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseInt8();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, sbyte value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, sbyte value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class Int16PropertyConverter : IJsonPropertyConverter<short>
public class Int16PropertyConverter : JsonPropertyConverter<short>
{
public short Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override short Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -31,18 +31,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseInt16();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, short value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, short value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class Int32PropertyConverter : IJsonPropertyConverter<int>
public class Int32PropertyConverter : JsonPropertyConverter<int>
{
public int Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override int Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -50,18 +50,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseInt32();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, int value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, int value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class Int64PropertyConverter : IJsonPropertyConverter<long>
public class Int64PropertyConverter : JsonPropertyConverter<long>
{
public long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -69,10 +69,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseInt64();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, long value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, long value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}


+ 11
- 11
src/Discord.Net.Serialization/Json/Converters/Primitives.String.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
/*public class CharPropertyConverter : IJsonPropertyConverter<char>
/*public class CharPropertyConverter : JsonPropertyConverter<char>
{
public char Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override char Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,18 +12,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return reader.ParseChar();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, char value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, char value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value.ToString());
writer.WriteValue(value);
}
}*/

public class StringPropertyConverter : IJsonPropertyConverter<string>
public class StringPropertyConverter : JsonPropertyConverter<string>
{
public string Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override string Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -33,10 +33,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected String");
return reader.ParseString();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, string value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, string value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}


+ 20
- 20
src/Discord.Net.Serialization/Json/Converters/Primitives.Unsigned.cs View File

@@ -2,9 +2,9 @@

namespace Discord.Serialization.Json.Converters
{
public class UInt8PropertyConverter : IJsonPropertyConverter<byte>
public class UInt8PropertyConverter : JsonPropertyConverter<byte>
{
public byte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override byte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -12,18 +12,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseUInt8();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, byte value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, byte value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class UInt16PropertyConverter : IJsonPropertyConverter<ushort>
public class UInt16PropertyConverter : JsonPropertyConverter<ushort>
{
public ushort Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override ushort Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -31,18 +31,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseUInt16();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, ushort value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, ushort value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class UInt32PropertyConverter : IJsonPropertyConverter<uint>
public class UInt32PropertyConverter : JsonPropertyConverter<uint>
{
public uint Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override uint Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -50,18 +50,18 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseUInt32();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, uint value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, uint value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value);
if (key != null)
writer.WriteAttribute(key, value);
else
writer.WriteValue(value);
}
}

public class UInt64PropertyConverter : IJsonPropertyConverter<ulong>
public class UInt64PropertyConverter : JsonPropertyConverter<ulong>
{
public ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
public override ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
if (isTopLevel)
reader.Read();
@@ -69,10 +69,10 @@ namespace Discord.Serialization.Json.Converters
throw new SerializationException("Bad input, expected Number or String");
return reader.ParseUInt64();
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, bool isTopLevel)
public override void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, string key)
{
if (isTopLevel)
writer.WriteAttribute(map.Key, value.ToString());
if (key != null)
writer.WriteAttribute(key, value.ToString());
else
writer.WriteValue(value.ToString());
}


+ 46
- 0
src/Discord.Net.Serialization/Json/Converters/Struct.cs View File

@@ -0,0 +1,46 @@
using System.Text.Json;

namespace Discord.Serialization.Json.Converters
{
/*public class StructPropertyConverter<T> : JsonPropertyConverter<T>
where T : struct, new()
{
private readonly ModelMap<T> _map;

public StructPropertyConverter(Serializer serializer)
{
_map = serializer.MapModel<T>();
}

public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel)
{
var subModel = new T();

if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartObject)
throw new SerializationException("Bad input, expected StartObject");
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
return subModel;
if (reader.TokenType != JsonTokenType.PropertyName)
throw new SerializationException("Bad input, expected PropertyName");
if (_map.TryGetProperty(reader.Value, out var property))
(property as IJsonPropertyMap<T>).Read(subModel, ref reader);
else
JsonReaderUtils.Skip(ref reader); //Unknown property, skip
}
throw new SerializationException("Bad input, expected EndObject");
}
public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key)
{
if (key != null)
writer.WriteObjectStart(key);
else
writer.WriteObjectStart();
for (int i = 0; i < _map.Properties.Length; i++)
(_map.Properties[i] as IJsonPropertyMap<T>).Write(value, ref writer);
writer.WriteObjectEnd();
}
}*/
}

+ 81
- 0
src/Discord.Net.Serialization/Json/DefaultJsonSerializer.cs View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Discord.Serialization.Json
{
public class DefaultJsonSerializer : JsonSerializer
{
private static readonly Lazy<DefaultJsonSerializer> _singleton = new Lazy<DefaultJsonSerializer>();
public static DefaultJsonSerializer Global => _singleton.Value;

public DefaultJsonSerializer()
: this((JsonSerializer)null) { }
public DefaultJsonSerializer(JsonSerializer parent)
: base(parent)
{
AddConverter<sbyte, Converters.Int8PropertyConverter>();
AddConverter<short, Converters.Int16PropertyConverter>();
AddConverter<int, Converters.Int32PropertyConverter>();
AddConverter<long, Converters.Int64PropertyConverter>();

AddConverter<byte, Converters.UInt8PropertyConverter>();
AddConverter<ushort, Converters.UInt16PropertyConverter>();
AddConverter<uint, Converters.UInt32PropertyConverter>();
AddConverter<ulong, Converters.UInt64PropertyConverter>();

AddConverter<float, Converters.SinglePropertyConverter>();
AddConverter<double, Converters.DoublePropertyConverter>();
AddConverter<decimal, Converters.DecimalPropertyConverter>();

//AddConverter<char, Converters.CharPropertyConverter>(); //TODO: char.Parse does not support Json.Net's serialization
AddConverter<string, Converters.StringPropertyConverter>();

AddConverter<DateTime, Converters.DateTimePropertyConverter>();
AddConverter<DateTimeOffset, Converters.DateTimeOffsetPropertyConverter>();

AddConverter<bool, Converters.BooleanPropertyConverter>();
AddConverter<Guid, Converters.GuidPropertyConverter>();

AddConverter<object, Converters.DynamicPropertyConverter>(
(type, prop) => prop.GetCustomAttributes<ModelSelectorAttribute>().Any());

AddGenericConverter(typeof(List<>), typeof(Converters.ListPropertyConverter<>));
//AddGenericConverter(typeof(IReadOnlyList<>), typeof(Converters.ListPropertyConverter<>));
//AddGenericConverter(typeof(IReadOnlyCollection<>), typeof(Converters.ListPropertyConverter<>));
//AddGenericConverter(typeof(IEnumerable<>), typeof(Converters.ListPropertyConverter<>));
AddGenericConverter(typeof(Dictionary<,>), typeof(Converters.DictionaryPropertyConverter<>));
//AddGenericConverter(typeof(IReadOnlyDictionary<,>), typeof(Converters.DictionaryPropertyConverter<>));
AddGenericConverter(typeof(Nullable<>), typeof(Converters.NullablePropertyConverter<>));

AddGenericConverter(typeof(Converters.ArrayPropertyConverter<>), //Arrays
(type, prop) => type.IsArray, innerType => innerType.GetElementType());
AddGenericConverter(typeof(Converters.StringEnumPropertyConverter<>), //Enums : string
(type, prop) => type.IsEnum && prop.GetCustomAttribute<ModelStringEnumAttribute>() != null);
AddGenericConverter(typeof(Converters.Int64EnumPropertyConverter<>), //Enums : sbyte/short/int/long
(type, prop) => type.IsEnum && IsSignedEnum(Enum.GetUnderlyingType(type.AsType())));
AddGenericConverter(typeof(Converters.UInt64EnumPropertyConverter<>), //Enums: byte/ushort/uint/ulong
(type, prop) => type.IsEnum && IsUnsignedEnum(Enum.GetUnderlyingType(type.AsType())));
AddGenericConverter(typeof(Converters.ObjectPropertyConverter<>), //Classes
(type, prop) => type.IsClass && type.DeclaredConstructors.Any(x => x.GetParameters().Length == 0));

//TODO: Structs?
}

private DefaultJsonSerializer(DefaultJsonSerializer parent)
: base(parent) { }
public DefaultJsonSerializer CreateScope() => new DefaultJsonSerializer(this);

private static bool IsSignedEnum(Type underlyingType)
=> underlyingType == typeof(sbyte) ||
underlyingType == typeof(short) ||
underlyingType == typeof(int) ||
underlyingType == typeof(long);
private static bool IsUnsignedEnum(Type underlyingType)
=> underlyingType == typeof(byte) ||
underlyingType == typeof(ushort) ||
underlyingType == typeof(uint) ||
underlyingType == typeof(ulong);
}
}

+ 13
- 2
src/Discord.Net.Serialization/Json/IJsonPropertyConverter.cs View File

@@ -2,7 +2,14 @@

namespace Discord.Serialization.Json
{
public interface IJsonPropertyConverter<T> : IJsonPropertyReader<T>, IJsonPropertyWriter<T> { }
public abstract class JsonPropertyConverter<T> : IJsonPropertyReader<T>, IJsonPropertyWriter<T>, IJsonPropertyWriter
{
public abstract T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel);
public abstract void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key);

void IJsonPropertyWriter.Write(PropertyMap map, object model, ref JsonWriter writer, object value, string key)
=> Write(map, model, ref writer, (T)value, key);
}

public interface IJsonPropertyReader<out T>
{
@@ -10,6 +17,10 @@ namespace Discord.Serialization.Json
}
public interface IJsonPropertyWriter<in T>
{
void Write(PropertyMap map, object model, ref JsonWriter writer, T value, bool isTopLevel);
void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key);
}
public interface IJsonPropertyWriter
{
void Write(PropertyMap map, object model, ref JsonWriter writer, object value, string key);
}
}

+ 8
- 8
src/Discord.Net.Serialization/Json/JsonPropertyMap.cs View File

@@ -15,11 +15,11 @@ namespace Discord.Serialization.Json

internal class JsonPropertyMap<TModel, TValue> : PropertyMap<TModel, TValue>, IJsonPropertyMap<TModel>
{
private readonly IJsonPropertyConverter<TValue> _converter;
private readonly JsonPropertyConverter<TValue> _converter;
private readonly Func<TModel, TValue> _getFunc;
private readonly Action<TModel, TValue> _setFunc;

public JsonPropertyMap(Serializer serializer, PropertyInfo propInfo, IJsonPropertyConverter<TValue> converter)
public JsonPropertyMap(Serializer serializer, PropertyInfo propInfo, JsonPropertyConverter<TValue> converter)
: base(serializer, propInfo)
{
_converter = converter;
@@ -28,17 +28,17 @@ namespace Discord.Serialization.Json
_setFunc = propInfo.SetMethod.CreateDelegate(typeof(Action<TModel, TValue>)) as Action<TModel, TValue>;
}

public void Read(TModel model, ref JsonReader reader)
{
var value = _converter.Read(this, model, ref reader, true);
_setFunc(model, value);
}
public void Write(TModel model, ref JsonWriter writer)
{
var value = _getFunc(model);
if (value == null && ExcludeNull)
return;
_converter.Write(this, model, ref writer, value, true);
}
public void Read(TModel model, ref JsonReader reader)
{
var value = _converter.Read(this, model, ref reader, true);
_setFunc(model, value);
_converter.Write(this, model, ref writer, value, Key);
}
}
}

+ 7
- 64
src/Discord.Net.Serialization/Json/JsonSerializer.cs View File

@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.Formatting;
@@ -8,64 +6,20 @@ using System.Text.Json;

namespace Discord.Serialization.Json
{
public class JsonSerializer : Serializer
public abstract class JsonSerializer : Serializer
{
private static readonly Lazy<JsonSerializer> _singleton = new Lazy<JsonSerializer>();
public static JsonSerializer Global => _singleton.Value;

public JsonSerializer()
{
AddConverter<sbyte, Converters.Int8PropertyConverter>();
AddConverter<short, Converters.Int16PropertyConverter>();
AddConverter<int, Converters.Int32PropertyConverter>();
AddConverter<long, Converters.Int64PropertyConverter>();

AddConverter<byte, Converters.UInt8PropertyConverter>();
AddConverter<ushort, Converters.UInt16PropertyConverter>();
AddConverter<uint, Converters.UInt32PropertyConverter>();
AddConverter<ulong, Converters.UInt64PropertyConverter>();

AddConverter<float, Converters.SinglePropertyConverter>();
AddConverter<double, Converters.DoublePropertyConverter>();
AddConverter<decimal, Converters.DecimalPropertyConverter>();
//AddConverter<char, Converters.CharPropertyConverter>(); //TODO: char.Parse does not support Json.Net's serialization
AddConverter<string, Converters.StringPropertyConverter>();

AddConverter<DateTime, Converters.DateTimePropertyConverter>();
AddConverter<DateTimeOffset, Converters.DateTimeOffsetPropertyConverter>();

AddConverter<bool, Converters.BooleanPropertyConverter>();
AddConverter<Guid, Converters.GuidPropertyConverter>();

AddConverter<object, Converters.DynamicPropertyConverter>(
(type, prop) => prop.GetCustomAttributes<ModelSelectorAttribute>().Any());

AddGenericConverter(typeof(List<>), typeof(Converters.ListPropertyConverter<>));
AddGenericConverter(typeof(Nullable<>), typeof(Converters.NullablePropertyConverter<>));

AddGenericConverter(typeof(Converters.StringEnumPropertyConverter<>),
(type, prop) => type.IsEnum && prop.GetCustomAttribute<ModelStringEnumAttribute>() != null);
AddGenericConverter(typeof(Converters.Int64EnumPropertyConverter<>),
(type, prop) => type.IsEnum && IsSignedEnum(Enum.GetUnderlyingType(type.AsType())));
AddGenericConverter(typeof(Converters.UInt64EnumPropertyConverter<>),
(type, prop) => type.IsEnum && IsUnsignedEnum(Enum.GetUnderlyingType(type.AsType())));
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>()
where TConverter : class, IJsonPropertyConverter<TValue>
where TConverter : JsonPropertyConverter<TValue>
=> AddConverter(typeof(TValue), typeof(TConverter));
public void AddConverter<TValue, TConverter>(Func<TypeInfo, PropertyInfo, bool> condition)
where TConverter : class, IJsonPropertyConverter<TValue>
where TConverter : JsonPropertyConverter<TValue>
=> AddConverter(typeof(TValue), typeof(TConverter), condition);

protected override PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo)
{
var converter = (IJsonPropertyConverter<TValue>)GetConverter(typeof(TValue), propInfo);
var converter = (JsonPropertyConverter<TValue>)GetConverter(typeof(TValue), propInfo);
return new JsonPropertyMap<TModel, TValue>(this, propInfo, converter);
}

@@ -74,25 +28,14 @@ namespace Discord.Serialization.Json
var reader = new JsonReader(data.Span, SymbolTable.InvariantUtf8);
if (!reader.Read())
return default;
var converter = GetConverter(typeof(TModel)) as IJsonPropertyConverter<TModel>;
var converter = GetConverter(typeof(TModel)) as JsonPropertyConverter<TModel>;
return converter.Read(null, null, ref reader, false);
}
public override void Write<TModel>(ArrayFormatter stream, TModel model)
{
var writer = new JsonWriter(stream);
var converter = GetConverter(typeof(TModel)) as IJsonPropertyConverter<TModel>;
converter.Write(null, null, ref writer, model, false);
var converter = GetConverter(typeof(TModel)) as JsonPropertyConverter<TModel>;
converter.Write(null, null, ref writer, model, null);
}

private static bool IsSignedEnum(Type underlyingType)
=> underlyingType == typeof(sbyte) ||
underlyingType == typeof(short) ||
underlyingType == typeof(int) ||
underlyingType == typeof(long);
private static bool IsUnsignedEnum(Type underlyingType)
=> underlyingType == typeof(byte) ||
underlyingType == typeof(ushort) ||
underlyingType == typeof(uint) ||
underlyingType == typeof(ulong);
}
}

+ 2
- 2
src/Discord.Net.Serialization/PropertyMap.cs View File

@@ -51,7 +51,7 @@ namespace Discord.Serialization
private TKey GetSelectorKey<TKey>(object model)
=> (_getSelectorFunc as Func<TModel, TKey>)((TModel)model);
public object GetDynamicConverter(object model)
=> _group.GetDynamicConverter(_getWrappedSelectorFunc, model);
=> _group?.GetDynamicConverter(_getWrappedSelectorFunc, model);
}
private readonly Delegate _getSelectorFunc, _getWrappedSelectorFunc;
@@ -73,7 +73,7 @@ namespace Discord.Serialization

return new Selector(prop, selectorGroup, selectorFunc);
})
.ToList();
.ToList();
}

public override object GetDynamicConverter(object model, bool throwOnMissing)


+ 20
- 51
src/Discord.Net.Serialization/Serializer.cs View File

@@ -13,65 +13,40 @@ namespace Discord.Serialization

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;
}
: this(null) { }
protected Serializer(Serializer parent)
{
_maps = parent._maps;
_converters = parent._converters;
IsScoped = true;
_maps = new ConcurrentDictionary<Type, object>();
_converters = new ConverterCollection(parent?._converters);
}

protected object GetConverter(Type valueType, PropertyInfo propInfo = null)
=> _converters.Get(valueType, propInfo);
protected internal object GetConverter(Type valueType, PropertyInfo propInfo = null)
=> _converters.Get(this, valueType, propInfo);

public void AddConverter(Type valueType, Type converterType)
{
CheckScoped();
_converters.Add(valueType, converterType);
}
=> _converters.Add(valueType, converterType);
public void AddConverter(Type valueType, Type converterType, Func<TypeInfo, PropertyInfo, bool> condition)
{
CheckScoped();
_converters.Add(valueType, converterType, condition);
}
=> _converters.Add(valueType, converterType, condition);

public void AddGenericConverter(Type converterType)
{
CheckScoped();
_converters.AddGeneric(converterType);
}
public void AddGenericConverter(Type converterType, Func<TypeInfo, PropertyInfo, bool> condition)
{
CheckScoped();
_converters.AddGeneric(converterType, condition);
}
public void AddGenericConverter(Type valueType, Type converterType)
{
CheckScoped();
_converters.AddGeneric(valueType, converterType);
}
public void AddGenericConverter(Type valueType, Type converterType, Func<TypeInfo, PropertyInfo, bool> condition)
{
CheckScoped();
_converters.AddGeneric(valueType, converterType, condition);
}
public void AddGenericConverter(Type converterType, Func<Type, Type> typeSelector = null)
=> _converters.AddGeneric(converterType, typeSelector);
public void AddGenericConverter(Type converterType, Func<TypeInfo, PropertyInfo, bool> condition, Func<Type, Type> typeSelector = null)
=> _converters.AddGeneric(converterType, condition, typeSelector);
public void AddGenericConverter(Type valueType, Type converterType, Func<Type, Type> typeSelector = null)
=> _converters.AddGeneric(valueType, converterType, typeSelector);
public void AddGenericConverter(Type valueType, Type converterType, Func<TypeInfo, PropertyInfo, bool> condition, Func<Type, Type> typeSelector = null)
=> _converters.AddGeneric(valueType, converterType, condition, typeSelector);

public void AddSelectorConverter(string groupKey, Type keyType, object keyValue, Type converterType)
{
CheckScoped();
_converters.AddSelector(groupKey, keyType, keyValue, converterType);
}
=> _converters.AddSelector(this, groupKey, keyType, keyValue, converterType);
public void AddSelectorConverter<TKey, TConverter>(string groupKey, TKey keyValue)
=> _converters.AddSelector(this, groupKey, typeof(TKey), keyValue, typeof(TConverter));
public ISelectorGroup GetSelectorGroup(Type keyType, string groupKey)
=> _converters.GetSelectorGroup(keyType, groupKey);

@@ -104,11 +79,5 @@ namespace Discord.Serialization

public abstract TModel Read<TModel>(ReadOnlyBuffer<byte> data);
public abstract void Write<TModel>(ArrayFormatter stream, TModel model);
private void CheckScoped()
{
if (IsScoped)
throw new InvalidOperationException("Scoped serializers are read-only");
}
}
}

+ 1
- 1
src/Discord.Net.Serialization/_corefxlab/System.Text.Json/System/Text/Json/JsonWriter.cs View File

@@ -314,7 +314,7 @@ namespace System.Text.Json
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void WriteStartAttribute(string name)
private void WriteStartAttribute(string name)
{
WriteItemSeperator();
_firstItem = false;


+ 1
- 1
src/Discord.Net.WebSocket/API/Gateway/IdentifyParams.cs View File

@@ -9,7 +9,7 @@ namespace Discord.API.Gateway
[ModelProperty("token")]
public string Token { get; set; }
[ModelProperty("properties")]
public IDictionary<string, string> Properties { get; set; }
public Dictionary<string, string> Properties { get; set; }
[ModelProperty("large_threshold")]
public int LargeThreshold { get; set; }
[ModelProperty("compress")]


+ 1
- 1
src/Discord.Net.WebSocket/API/Gateway/MessageDeleteBulkEvent.cs View File

@@ -9,6 +9,6 @@ namespace Discord.API.Gateway
[ModelProperty("channel_id")]
public ulong ChannelId { get; set; }
[ModelProperty("ids")]
public IEnumerable<ulong> Ids { get; set; }
public ulong[] Ids { get; set; }
}
}

+ 1
- 1
src/Discord.Net.WebSocket/API/Gateway/RequestMembersParams.cs View File

@@ -12,6 +12,6 @@ namespace Discord.API.Gateway
public int Limit { get; set; }

[ModelProperty("guild_id")]
public IEnumerable<ulong> GuildIds { get; set; }
public ulong[] GuildIds { get; set; }
}
}

+ 2
- 2
src/Discord.Net.WebSocket/Audio/AudioClient.cs View File

@@ -67,7 +67,7 @@ namespace Discord.Audio
ChannelId = channelId;
_audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}");

_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordVoiceJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_audioLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();
@@ -128,7 +128,7 @@ namespace Discord.Audio
await ApiClient.ConnectAsync("wss://" + _url).ConfigureAwait(false);
await _audioLogger.DebugAsync("Listening on port " + ApiClient.UdpPort).ConfigureAwait(false);
await _audioLogger.DebugAsync("Sending Identity").ConfigureAwait(false);
await ApiClient.SendIdentityAsync(_userId, _sessionId, _token).ConfigureAwait(false);
await ApiClient.SendIdentifyAsync(_userId, _sessionId, _token).ConfigureAwait(false);

//Wait for READY
await _connection.WaitAsync().ConfigureAwait(false);


+ 1
- 1
src/Discord.Net.WebSocket/DiscordShardedClient.cs View File

@@ -55,7 +55,7 @@ namespace Discord.WebSocket
_baseConfig = config;
_connectionGroupLock = new SemaphoreSlim(1, 1);

_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordSocketJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();


+ 14
- 6
src/Discord.Net.WebSocket/DiscordSocketApiClient.cs View File

@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Text.Formatting;
using System.Threading;
@@ -183,8 +184,9 @@ namespace Discord.API
}
finally
{
data.Clear();
_formatters.Enqueue(data);
}
}
}

//Gateway
@@ -236,31 +238,37 @@ namespace Discord.API
public async Task SendStatusUpdateAsync(UserStatus status, bool isAFK, long? since, Game game, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);
var args = new StatusUpdateParams
var msg = new StatusUpdateParams
{
Status = status,
IdleSince = since,
IsAFK = isAFK,
Game = game
};
await SendGatewayAsync(GatewayOpCode.StatusUpdate, args, options: options).ConfigureAwait(false);
await SendGatewayAsync(GatewayOpCode.StatusUpdate, msg, options: options).ConfigureAwait(false);
}
public async Task SendRequestMembersAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, new RequestMembersParams { GuildIds = guildIds, Query = "", Limit = 0 }, options: options).ConfigureAwait(false);
var msg = new RequestMembersParams
{
GuildIds = guildIds.ToArray(),
Query = "",
Limit = 0
};
await SendGatewayAsync(GatewayOpCode.RequestGuildMembers, msg, options: options).ConfigureAwait(false);
}
public async Task SendVoiceStateUpdateAsync(ulong guildId, ulong? channelId, bool selfDeaf, bool selfMute, RequestOptions options = null)
{
options = RequestOptions.CreateOrClone(options);
var payload = new VoiceStateUpdateParams
var msg = new VoiceStateUpdateParams
{
GuildId = guildId,
ChannelId = channelId,
SelfDeaf = selfDeaf,
SelfMute = selfMute
};
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, payload, options: options).ConfigureAwait(false);
await SendGatewayAsync(GatewayOpCode.VoiceStateUpdate, msg, options: options).ConfigureAwait(false);
}
public async Task SendGuildSyncAsync(IEnumerable<ulong> guildIds, RequestOptions options = null)
{


+ 31
- 32
src/Discord.Net.WebSocket/DiscordSocketClient.cs View File

@@ -12,7 +12,6 @@ using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using GameModel = Discord.API.Game;
@@ -88,7 +87,7 @@ namespace Discord.WebSocket
_stateLock = new SemaphoreSlim(1, 1);
_gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}");

_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordSocketJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();
@@ -593,7 +592,7 @@ namespace Discord.WebSocket
{
await _gatewayLogger.DebugAsync("Received Dispatch (GUILD_EMOJIS_UPDATE)").ConfigureAwait(false);

var data = payload as API.Gateway.GuildEmojiUpdateEvent;
var data = payload as GuildEmojiUpdateEvent;
var guild = State.GetGuild(data.GuildId);
if (guild != null)
{
@@ -1201,6 +1200,35 @@ namespace Discord.WebSocket
}
}
break;
case "MESSAGE_DELETE_BULK":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);

var data = payload as MessageDeleteBulkEvent;
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
if (!(guild?.IsSynced ?? true))
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}

foreach (ulong id in data.Ids)
{
var msg = SocketChannelHelper.RemoveMessage(channel, this, id);
bool isCached = msg != null;
var cacheable = new Cacheable<IMessage, ulong>(msg, id, isCached, async () => await channel.GetMessageAsync(id));
await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false);
}
}
else
{
await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
return;
}
}
break;
case "MESSAGE_REACTION_ADD":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_REACTION_ADD)").ConfigureAwait(false);
@@ -1271,35 +1299,6 @@ namespace Discord.WebSocket
}
}
break;
case "MESSAGE_DELETE_BULK":
{
await _gatewayLogger.DebugAsync("Received Dispatch (MESSAGE_DELETE_BULK)").ConfigureAwait(false);

var data = payload as MessageDeleteBulkEvent;
if (State.GetChannel(data.ChannelId) is ISocketMessageChannel channel)
{
var guild = (channel as SocketGuildChannel)?.Guild;
if (!(guild?.IsSynced ?? true))
{
await UnsyncedGuildAsync(type, guild.Id).ConfigureAwait(false);
return;
}

foreach (ulong id in data.Ids)
{
var msg = SocketChannelHelper.RemoveMessage(channel, this, id);
bool isCached = msg != null;
var cacheable = new Cacheable<IMessage, ulong>(msg, id, isCached, async () => await channel.GetMessageAsync(id));
await TimedInvokeAsync(_messageDeletedEvent, nameof(MessageDeleted), cacheable, channel).ConfigureAwait(false);
}
}
else
{
await UnknownChannelAsync(type, data.ChannelId).ConfigureAwait(false);
return;
}
}
break;

//Statuses
case "PRESENCE_UPDATE":


+ 2
- 1
src/Discord.Net.WebSocket/DiscordVoiceApiClient.cs View File

@@ -124,6 +124,7 @@ namespace Discord.Audio
}
finally
{
data.Clear();
_formatters.Enqueue(data);
}
}
@@ -138,7 +139,7 @@ namespace Discord.Audio
{
await SendAsync(VoiceOpCode.Heartbeat, DateTimeUtils.ToUnixMilliseconds(DateTimeOffset.UtcNow), options: options).ConfigureAwait(false);
}
public async Task SendIdentityAsync(ulong userId, string sessionId, string token)
public async Task SendIdentifyAsync(ulong userId, string sessionId, string token)
{
await SendAsync(VoiceOpCode.Identify, new IdentifyParams
{


+ 119
- 0
src/Discord.Net.WebSocket/Serialization/Json/DiscordSocketJsonSerializer.cs View File

@@ -0,0 +1,119 @@
using Discord.API.Gateway;
using Discord.Serialization.Json.Converters;
using System;

namespace Discord.Serialization.Json
{
public class DiscordSocketJsonSerializer : JsonSerializer
{
private static readonly Lazy<DiscordSocketJsonSerializer> _singleton = new Lazy<DiscordSocketJsonSerializer>();
public static DiscordSocketJsonSerializer Global => _singleton.Value;

public DiscordSocketJsonSerializer()
: this((JsonSerializer)null) { }
public DiscordSocketJsonSerializer(JsonSerializer parent)
: base(parent ?? DiscordRestJsonSerializer.Global)
{
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<HelloEvent>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.Hello);
AddSelectorConverter<GatewayOpCode, BooleanPropertyConverter>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.InvalidSession);

AddSelectorConverter<GatewayOpCode, Int32PropertyConverter>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.Heartbeat);
AddSelectorConverter<GatewayOpCode, Int32PropertyConverter>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.HeartbeatAck);
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<IdentifyParams>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.Identify);
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<ResumeParams>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.Resume);
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<StatusUpdateParams>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.StatusUpdate);
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<RequestMembersParams>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.RequestGuildMembers);
AddSelectorConverter<GatewayOpCode, ObjectPropertyConverter<VoiceStateUpdateParams>>(
ModelSelectorGroups.GatewayFrame, GatewayOpCode.VoiceStateUpdate);

AddSelectorConverter<string, ObjectPropertyConverter<ReadyEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "READY");

AddSelectorConverter<string, ObjectPropertyConverter<ExtendedGuild>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_CREATE");
AddSelectorConverter<string, ObjectPropertyConverter<API.Guild>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildEmojiUpdateEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_EMOJIS_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildSyncEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_SYNC");
AddSelectorConverter<string, ObjectPropertyConverter<ExtendedGuild>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_DELETE");

AddSelectorConverter<string, ObjectPropertyConverter<API.Channel>>(
ModelSelectorGroups.GatewayDispatchFrame, "CHANNEL_CREATE");
AddSelectorConverter<string, ObjectPropertyConverter<API.Channel>>(
ModelSelectorGroups.GatewayDispatchFrame, "CHANNEL_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<API.Channel>>(
ModelSelectorGroups.GatewayDispatchFrame, "CHANNEL_DELETE");

AddSelectorConverter<string, ObjectPropertyConverter<GuildMemberAddEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_MEMBER_ADD");
AddSelectorConverter<string, ObjectPropertyConverter<GuildMemberUpdateEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_MEMBER_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildMemberRemoveEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_MEMBER_REMOVE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildMembersChunkEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_MEMBERS_CHUNK");

AddSelectorConverter<string, ObjectPropertyConverter<RecipientEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "CHANNEL_RECIPIENT_ADD");
AddSelectorConverter<string, ObjectPropertyConverter<RecipientEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "CHANNEL_RECIPIENT_REMOVE");

AddSelectorConverter<string, ObjectPropertyConverter<GuildRoleCreateEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_ROLE_CREATE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildRoleUpdateEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_ROLE_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<GuildRoleDeleteEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_ROLE_UPDATE");

AddSelectorConverter<string, ObjectPropertyConverter<GuildBanEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_BAN_ADD");
AddSelectorConverter<string, ObjectPropertyConverter<GuildBanEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "GUILD_BAN_REMOVE");

AddSelectorConverter<string, ObjectPropertyConverter<API.Message>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_CREATE");
AddSelectorConverter<string, ObjectPropertyConverter<API.Message>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<API.Message>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_DELETE");
AddSelectorConverter<string, ObjectPropertyConverter<MessageDeleteBulkEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_DELETE_BULK");

AddSelectorConverter<string, ObjectPropertyConverter<Reaction>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_REACTION_ADD");
AddSelectorConverter<string, ObjectPropertyConverter<Reaction>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_REACTION_REMOVE");
AddSelectorConverter<string, ObjectPropertyConverter<RemoveAllReactionsEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "MESSAGE_REACTION_REMOVE_ALL");

AddSelectorConverter<string, ObjectPropertyConverter<API.Presence>>(
ModelSelectorGroups.GatewayDispatchFrame, "PRESENCE_UPDATE");

AddSelectorConverter<string, ObjectPropertyConverter<API.User>>(
ModelSelectorGroups.GatewayDispatchFrame, "USER_UPDATE");

AddSelectorConverter<string, ObjectPropertyConverter<TypingStartEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "TYPING_START");

AddSelectorConverter<string, ObjectPropertyConverter<API.VoiceState>>(
ModelSelectorGroups.GatewayDispatchFrame, "VOICE_STATE_UPDATE");
AddSelectorConverter<string, ObjectPropertyConverter<VoiceServerUpdateEvent>>(
ModelSelectorGroups.GatewayDispatchFrame, "VOICE_SERVER_UPDATE");
}

private DiscordSocketJsonSerializer(DiscordSocketJsonSerializer parent)
: base(parent) { }
public DiscordSocketJsonSerializer CreateScope() => new DiscordSocketJsonSerializer(this);
}
}

+ 21
- 0
src/Discord.Net.WebSocket/Serialization/Json/DiscordVoiceJsonSerializer.cs View File

@@ -0,0 +1,21 @@
using System;

namespace Discord.Serialization.Json
{
public class DiscordVoiceJsonSerializer : JsonSerializer
{
private static readonly Lazy<DiscordVoiceJsonSerializer> _singleton = new Lazy<DiscordVoiceJsonSerializer>();
public static DiscordVoiceJsonSerializer Global => _singleton.Value;

public DiscordVoiceJsonSerializer()
: this((JsonSerializer)null) { }
public DiscordVoiceJsonSerializer(JsonSerializer parent)
: base(parent ?? DiscordRestJsonSerializer.Global)
{
}

private DiscordVoiceJsonSerializer(DiscordVoiceJsonSerializer parent)
: base(parent) { }
public DiscordVoiceJsonSerializer CreateScope() => new DiscordVoiceJsonSerializer(this);
}
}

+ 1
- 1
src/Discord.Net.Webhook/DiscordWebhookClient.cs View File

@@ -30,7 +30,7 @@ namespace Discord.Webhook
{
_webhookId = webhookId;

_serializer = DiscordJsonSerializer.Global.CreateScope();
_serializer = DiscordRestJsonSerializer.Global.CreateScope();
_serializer.Error += ex =>
{
_restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult();


Loading…
Cancel
Save