Browse Source

Improved Json.Net contract resolving logic with optionals

pull/108/head
RogueException 9 years ago
parent
commit
851c60fcbf
3 changed files with 83 additions and 52 deletions
  1. +63
    -45
      src/Discord.Net/Net/Converters/DiscordContractResolver.cs
  2. +1
    -4
      src/Discord.Net/Net/Converters/ImageConverter.cs
  3. +19
    -3
      src/Discord.Net/Net/Converters/OptionalConverter.cs

+ 63
- 45
src/Discord.Net/Net/Converters/DiscordContractResolver.cs View File

@@ -3,6 +3,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

@@ -17,59 +18,34 @@ namespace Discord.Net.Converters
{
var property = base.CreateProperty(member, memberSerialization);
var propInfo = member as PropertyInfo;

if (propInfo != null)
{
JsonConverter converter = null;
var type = property.PropertyType;
var typeInfo = type.GetTypeInfo();
JsonConverter converter;
var type = propInfo.PropertyType;

//Primitives
if (propInfo.GetCustomAttribute<Int53Attribute>() == null)
if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>))
{
if (type == typeof(ulong))
converter = UInt64Converter.Instance;
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;
var typeInput = propInfo.DeclaringType;
var innerTypeOutput = type.GenericTypeArguments[0];

//Entities
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>)))
converter = UInt64EntityConverter.Instance;
else if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<string>)))
converter = StringEntityConverter.Instance;
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);

//Special
else if (type == typeof(string) && propInfo.GetCustomAttribute<ImageAttribute>() != null)
converter = ImageConverter.Instance;
else if (type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Optional<>))
var converterType = typeof(OptionalConverter<>).MakeGenericType(innerTypeOutput).GetTypeInfo();
var instanceField = converterType.GetDeclaredField("Instance");
converter = instanceField.GetValue(null) as JsonConverter;
if (converter == null)
{
var typeInput = propInfo.DeclaringType;
var innerTypeOutput = type.GenericTypeArguments[0];

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;
var innerConverter = GetConverter(propInfo, innerTypeOutput);
converter = converterType.DeclaredConstructors.FirstOrDefault().Invoke(new object[] { innerConverter }) as JsonConverter;
instanceField.SetValue(null, converter);
}
}
else
converter = GetConverter(propInfo, type);

if (converter != null)
{
@@ -77,10 +53,52 @@ namespace Discord.Net.Converters
property.MemberConverter = converter;
}
}

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)
{
return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified;


+ 1
- 4
src/Discord.Net/Net/Converters/ImageConverter.cs View File

@@ -1,5 +1,4 @@
using Discord.API;
using Newtonsoft.Json;
using Newtonsoft.Json;
using System;
using System.IO;

@@ -20,8 +19,6 @@ namespace Discord.Net.Converters

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value is Optional<Stream>)
value = (Optional<Stream>)value;
var stream = value as Stream;

byte[] bytes = new byte[stream.Length - stream.Position];


+ 19
- 3
src/Discord.Net/Net/Converters/OptionalConverter.cs View File

@@ -5,20 +5,36 @@ namespace Discord.Net.Converters
{
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 CanRead => true;
public override bool CanWrite => true;

public OptionalConverter(JsonConverter innerConverter)
{
_innerConverter = innerConverter;
}

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)
{
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));
}
}
}

Loading…
Cancel
Save