Improved Json.Net contract resolving logic with optionals
This commit is contained in:
@@ -3,6 +3,7 @@ using Newtonsoft.Json;
|
|||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
@@ -17,59 +18,34 @@ namespace Discord.Net.Converters
|
|||||||
{
|
{
|
||||||
var property = base.CreateProperty(member, memberSerialization);
|
var property = base.CreateProperty(member, memberSerialization);
|
||||||
var propInfo = member as PropertyInfo;
|
var propInfo = member as PropertyInfo;
|
||||||
|
|
||||||
if (propInfo != null)
|
if (propInfo != null)
|
||||||
{
|
{
|
||||||
JsonConverter converter = null;
|
JsonConverter converter;
|
||||||
var type = property.PropertyType;
|
var type = propInfo.PropertyType;
|
||||||
var typeInfo = type.GetTypeInfo();
|
|
||||||
|
|
||||||
//Primitives
|
if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>))
|
||||||
if (propInfo.GetCustomAttribute<Int53Attribute>() == null)
|
|
||||||
{
|
{
|
||||||
if (type == typeof(ulong))
|
var typeInput = propInfo.DeclaringType;
|
||||||
converter = UInt64Converter.Instance;
|
var innerTypeOutput = type.GenericTypeArguments[0];
|
||||||
else if (type == typeof(ulong?))
|
|
||||||
converter = NullableUInt64Converter.Instance;
|
|
||||||
else if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEnumerable<ulong>)))
|
|
||||||
converter = UInt64ArrayConverter.Instance;
|
|
||||||
}
|
|
||||||
if (converter == null)
|
|
||||||
{
|
|
||||||
//Enums
|
|
||||||
if (type == typeof(ChannelType))
|
|
||||||
converter = ChannelTypeConverter.Instance;
|
|
||||||
else if (type == typeof(PermissionTarget))
|
|
||||||
converter = PermissionTargetConverter.Instance;
|
|
||||||
else if (type == typeof(UserStatus))
|
|
||||||
converter = UserStatusConverter.Instance;
|
|
||||||
else if (type == typeof(Direction))
|
|
||||||
converter = DirectionConverter.Instance;
|
|
||||||
|
|
||||||
//Entities
|
var getter = typeof(Func<,>).MakeGenericType(typeInput, type);
|
||||||
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>)))
|
var getterDelegate = propInfo.GetMethod.CreateDelegate(getter);
|
||||||
converter = UInt64EntityConverter.Instance;
|
var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, innerTypeOutput);
|
||||||
else if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<string>)))
|
var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>));
|
||||||
converter = StringEntityConverter.Instance;
|
property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate);
|
||||||
|
|
||||||
//Special
|
var converterType = typeof(OptionalConverter<>).MakeGenericType(innerTypeOutput).GetTypeInfo();
|
||||||
else if (type == typeof(string) && propInfo.GetCustomAttribute<ImageAttribute>() != null)
|
var instanceField = converterType.GetDeclaredField("Instance");
|
||||||
converter = ImageConverter.Instance;
|
converter = instanceField.GetValue(null) as JsonConverter;
|
||||||
else if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>))
|
if (converter == null)
|
||||||
{
|
{
|
||||||
var typeInput = propInfo.DeclaringType;
|
var innerConverter = GetConverter(propInfo, innerTypeOutput);
|
||||||
var innerTypeOutput = type.GenericTypeArguments[0];
|
converter = converterType.DeclaredConstructors.FirstOrDefault().Invoke(new object[] { innerConverter }) as JsonConverter;
|
||||||
|
instanceField.SetValue(null, converter);
|
||||||
var getter = typeof(Func<,>).MakeGenericType(typeInput, type);
|
|
||||||
var getterDelegate = propInfo.GetMethod.CreateDelegate(getter);
|
|
||||||
var shouldSerialize = _shouldSerialize.MakeGenericMethod(typeInput, innerTypeOutput);
|
|
||||||
var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>));
|
|
||||||
property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate);
|
|
||||||
|
|
||||||
var converterType = typeof(OptionalConverter<>).MakeGenericType(innerTypeOutput);
|
|
||||||
converter = converterType.GetTypeInfo().GetDeclaredField("Instance").GetValue(null) as JsonConverter;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
converter = GetConverter(propInfo, type);
|
||||||
|
|
||||||
if (converter != null)
|
if (converter != null)
|
||||||
{
|
{
|
||||||
@@ -77,10 +53,52 @@ namespace Discord.Net.Converters
|
|||||||
property.MemberConverter = converter;
|
property.MemberConverter = converter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsonConverter GetConverter(PropertyInfo propInfo, Type type, TypeInfo typeInfo = null)
|
||||||
|
{
|
||||||
|
bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null;
|
||||||
|
|
||||||
|
//Primitives
|
||||||
|
if (!hasInt53)
|
||||||
|
{
|
||||||
|
if (type == typeof(ulong))
|
||||||
|
return UInt64Converter.Instance;
|
||||||
|
if (type == typeof(ulong?))
|
||||||
|
return NullableUInt64Converter.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Enums
|
||||||
|
if (type == typeof(ChannelType))
|
||||||
|
return ChannelTypeConverter.Instance;
|
||||||
|
if (type == typeof(PermissionTarget))
|
||||||
|
return PermissionTargetConverter.Instance;
|
||||||
|
if (type == typeof(UserStatus))
|
||||||
|
return UserStatusConverter.Instance;
|
||||||
|
if (type == typeof(Direction))
|
||||||
|
return DirectionConverter.Instance;
|
||||||
|
|
||||||
|
//Special
|
||||||
|
if (type == typeof(Stream) && propInfo.GetCustomAttribute<ImageAttribute>() != null)
|
||||||
|
return ImageConverter.Instance;
|
||||||
|
|
||||||
|
|
||||||
|
if (typeInfo == null) typeInfo = type.GetTypeInfo();
|
||||||
|
|
||||||
|
//Primitives
|
||||||
|
if (!hasInt53 && typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEnumerable<ulong>)))
|
||||||
|
return UInt64ArrayConverter.Instance;
|
||||||
|
|
||||||
|
//Entities
|
||||||
|
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>)))
|
||||||
|
return UInt64EntityConverter.Instance;
|
||||||
|
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<string>)))
|
||||||
|
return StringEntityConverter.Instance;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ShouldSerialize<TOwner, TValue>(object owner, Delegate getter)
|
private static bool ShouldSerialize<TOwner, TValue>(object owner, Delegate getter)
|
||||||
{
|
{
|
||||||
return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified;
|
return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Discord.API;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
@@ -20,8 +19,6 @@ namespace Discord.Net.Converters
|
|||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
if (value is Optional<Stream>)
|
|
||||||
value = (Optional<Stream>)value;
|
|
||||||
var stream = value as Stream;
|
var stream = value as Stream;
|
||||||
|
|
||||||
byte[] bytes = new byte[stream.Length - stream.Position];
|
byte[] bytes = new byte[stream.Length - stream.Position];
|
||||||
|
|||||||
@@ -5,20 +5,36 @@ namespace Discord.Net.Converters
|
|||||||
{
|
{
|
||||||
public class OptionalConverter<T> : JsonConverter
|
public class OptionalConverter<T> : JsonConverter
|
||||||
{
|
{
|
||||||
public static readonly OptionalConverter<T> Instance = new OptionalConverter<T>();
|
public static OptionalConverter<T> Instance;
|
||||||
|
|
||||||
|
private readonly JsonConverter _innerConverter;
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType) => true;
|
public override bool CanConvert(Type objectType) => true;
|
||||||
public override bool CanRead => true;
|
public override bool CanRead => true;
|
||||||
public override bool CanWrite => true;
|
public override bool CanWrite => true;
|
||||||
|
|
||||||
|
public OptionalConverter(JsonConverter innerConverter)
|
||||||
|
{
|
||||||
|
_innerConverter = innerConverter;
|
||||||
|
}
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
return new Optional<T>(serializer.Deserialize<T>(reader));
|
T obj;
|
||||||
|
if (_innerConverter != null)
|
||||||
|
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer);
|
||||||
|
else
|
||||||
|
obj = serializer.Deserialize<T>(reader);
|
||||||
|
return new Optional<T>(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
serializer.Serialize(writer, ((Optional<T>)value).Value);
|
value = ((Optional<T>)value).Value;
|
||||||
|
if (_innerConverter != null)
|
||||||
|
_innerConverter.WriteJson(writer, value, serializer);
|
||||||
|
else
|
||||||
|
serializer.Serialize(writer, value, typeof(T));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user