@@ -27,7 +27,7 @@ namespace Discord.API | |||||
[JsonProperty("mention_everyone")] | [JsonProperty("mention_everyone")] | ||||
public Optional<bool> MentionEveryone { get; set; } | public Optional<bool> MentionEveryone { get; set; } | ||||
[JsonProperty("mentions")] | [JsonProperty("mentions")] | ||||
public Optional<ObjectOrId<User>[]> UserMentions { get; set; } | |||||
public Optional<EntityOrId<User>[]> UserMentions { get; set; } | |||||
[JsonProperty("mention_roles")] | [JsonProperty("mention_roles")] | ||||
public Optional<ulong[]> RoleMentions { get; set; } | public Optional<ulong[]> RoleMentions { get; set; } | ||||
[JsonProperty("attachments")] | [JsonProperty("attachments")] | ||||
@@ -1,16 +1,16 @@ | |||||
namespace Discord.API | namespace Discord.API | ||||
{ | { | ||||
public struct ObjectOrId<T> | |||||
public struct EntityOrId<T> | |||||
{ | { | ||||
public ulong Id { get; } | public ulong Id { get; } | ||||
public T Object { get; } | public T Object { get; } | ||||
public ObjectOrId(ulong id) | |||||
public EntityOrId(ulong id) | |||||
{ | { | ||||
Id = id; | Id = id; | ||||
Object = default(T); | Object = default(T); | ||||
} | } | ||||
public ObjectOrId(T obj) | |||||
public EntityOrId(T obj) | |||||
{ | { | ||||
Id = 0; | Id = 0; | ||||
Object = obj; | Object = obj; |
@@ -1,28 +1,38 @@ | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Globalization; | |||||
namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
{ | { | ||||
internal class UInt64ArrayConverter : JsonConverter | |||||
internal class ArrayConverter<T> : JsonConverter | |||||
{ | { | ||||
public static readonly UInt64ArrayConverter Instance = new UInt64ArrayConverter(); | |||||
public static ArrayConverter<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 ArrayConverter(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) | ||||
{ | { | ||||
var result = new List<ulong>(); | |||||
var result = new List<T>(); | |||||
if (reader.TokenType == JsonToken.StartArray) | if (reader.TokenType == JsonToken.StartArray) | ||||
{ | { | ||||
reader.Read(); | reader.Read(); | ||||
while (reader.TokenType != JsonToken.EndArray) | while (reader.TokenType != JsonToken.EndArray) | ||||
{ | { | ||||
ulong id = ulong.Parse((string)reader.Value, NumberStyles.None, CultureInfo.InvariantCulture); | |||||
result.Add(id); | |||||
T obj; | |||||
if (_innerConverter != null) | |||||
obj = (T)_innerConverter.ReadJson(reader, typeof(T), null, serializer); | |||||
else | |||||
obj = serializer.Deserialize<T>(reader); | |||||
result.Add(obj); | |||||
reader.Read(); | reader.Read(); | ||||
} | } | ||||
} | } | ||||
@@ -33,9 +43,15 @@ namespace Discord.Net.Converters | |||||
if (value != null) | if (value != null) | ||||
{ | { | ||||
writer.WriteStartArray(); | writer.WriteStartArray(); | ||||
var a = (ulong[])value; | |||||
var a = (T[])value; | |||||
for (int i = 0; i < a.Length; i++) | for (int i = 0; i < a.Length; i++) | ||||
writer.WriteValue(a[i].ToString(CultureInfo.InvariantCulture)); | |||||
{ | |||||
if (_innerConverter != null) | |||||
_innerConverter.WriteJson(writer, a[i], serializer); | |||||
else | |||||
serializer.Serialize(writer, a[i], typeof(T)); | |||||
} | |||||
writer.WriteEndArray(); | writer.WriteEndArray(); | ||||
} | } | ||||
else | else |
@@ -23,7 +23,7 @@ namespace Discord.Net.Converters | |||||
if (propInfo != null) | if (propInfo != null) | ||||
{ | { | ||||
JsonConverter converter; | JsonConverter converter; | ||||
var type = propInfo.PropertyType; | |||||
Type type = propInfo.PropertyType; | |||||
Type genericType = type.IsConstructedGenericType ? type.GetGenericTypeDefinition() : null; | Type genericType = type.IsConstructedGenericType ? type.GetGenericTypeDefinition() : null; | ||||
if (genericType == typeof(Optional<>)) | if (genericType == typeof(Optional<>)) | ||||
@@ -37,29 +37,7 @@ namespace Discord.Net.Converters | |||||
var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>)); | var shouldSerializeDelegate = (Func<object, Delegate, bool>)shouldSerialize.CreateDelegate(typeof(Func<object, Delegate, bool>)); | ||||
property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); | property.ShouldSerialize = x => shouldSerializeDelegate(x, getterDelegate); | ||||
var converterType = typeof(OptionalConverter<>).MakeGenericType(innerTypeOutput).GetTypeInfo(); | |||||
var instanceField = converterType.GetDeclaredField("Instance"); | |||||
converter = instanceField.GetValue(null) as JsonConverter; | |||||
if (converter == null) | |||||
{ | |||||
var innerConverter = GetConverter(propInfo, innerTypeOutput); | |||||
converter = converterType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; | |||||
instanceField.SetValue(null, converter); | |||||
} | |||||
} | |||||
else if (genericType == typeof(ObjectOrId<>)) | |||||
{ | |||||
var innerTypeOutput = type.GenericTypeArguments[0]; | |||||
var converterType = typeof(ObjectOrIdConverter<>).MakeGenericType(innerTypeOutput).GetTypeInfo(); | |||||
var instanceField = converterType.GetDeclaredField("Instance"); | |||||
converter = instanceField.GetValue(null) as JsonConverter; | |||||
if (converter == null) | |||||
{ | |||||
var innerConverter = GetConverter(propInfo, innerTypeOutput); | |||||
converter = converterType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; | |||||
instanceField.SetValue(null, converter); | |||||
} | |||||
converter = MakeGenericConverter(propInfo, typeof(OptionalConverter<>), innerTypeOutput); | |||||
} | } | ||||
else | else | ||||
converter = GetConverter(propInfo, type); | converter = GetConverter(propInfo, type); | ||||
@@ -75,9 +53,18 @@ namespace Discord.Net.Converters | |||||
return property; | return property; | ||||
} | } | ||||
private JsonConverter GetConverter(MemberInfo member, Type type, TypeInfo typeInfo = null) | |||||
private static JsonConverter GetConverter(PropertyInfo propInfo, Type type, TypeInfo typeInfo = null, int depth = 0) | |||||
{ | { | ||||
bool hasInt53 = member.GetCustomAttribute<Int53Attribute>() != null; | |||||
if (type.IsArray) | |||||
return MakeGenericConverter(propInfo, typeof(ArrayConverter<>), type.GetElementType()); | |||||
if (type.IsConstructedGenericType) | |||||
{ | |||||
Type genericType = type.GetGenericTypeDefinition(); | |||||
if (genericType == typeof(EntityOrId<>)) | |||||
return MakeGenericConverter(propInfo, typeof(UInt64EntityOrIdConverter<>), type.GenericTypeArguments[0]); | |||||
} | |||||
bool hasInt53 = propInfo.GetCustomAttribute<Int53Attribute>() != null; | |||||
//Primitives | //Primitives | ||||
if (!hasInt53) | if (!hasInt53) | ||||
@@ -100,10 +87,6 @@ namespace Discord.Net.Converters | |||||
if (typeInfo == null) typeInfo = type.GetTypeInfo(); | if (typeInfo == null) typeInfo = type.GetTypeInfo(); | ||||
//Primitives | |||||
if (!hasInt53 && typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEnumerable<ulong>))) | |||||
return UInt64ArrayConverter.Instance; | |||||
//Entities | //Entities | ||||
if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>))) | if (typeInfo.ImplementedInterfaces.Any(x => x == typeof(IEntity<ulong>))) | ||||
return UInt64EntityConverter.Instance; | return UInt64EntityConverter.Instance; | ||||
@@ -117,5 +100,19 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified; | return (getter as Func<TOwner, Optional<TValue>>)((TOwner)owner).IsSpecified; | ||||
} | } | ||||
private static JsonConverter MakeGenericConverter(PropertyInfo propInfo, Type converterType, Type innerType) | |||||
{ | |||||
var genericType = converterType.MakeGenericType(innerType).GetTypeInfo(); | |||||
var instanceField = genericType.GetDeclaredField("Instance"); | |||||
var converter = instanceField.GetValue(null) as JsonConverter; | |||||
if (converter == null) | |||||
{ | |||||
var innerConverter = GetConverter(propInfo, innerType); | |||||
converter = genericType.DeclaredConstructors.First().Invoke(new object[] { innerConverter }) as JsonConverter; | |||||
instanceField.SetValue(null, converter); | |||||
} | |||||
return converter; | |||||
} | |||||
} | } | ||||
} | } |
@@ -4,9 +4,9 @@ using System; | |||||
namespace Discord.Net.Converters | namespace Discord.Net.Converters | ||||
{ | { | ||||
internal class ObjectOrIdConverter<T> : JsonConverter | |||||
internal class UInt64EntityOrIdConverter<T> : JsonConverter | |||||
{ | { | ||||
internal static ObjectOrIdConverter<T> Instance; | |||||
public static UInt64EntityOrIdConverter<T> Instance; | |||||
private readonly JsonConverter _innerConverter; | private readonly JsonConverter _innerConverter; | ||||
@@ -14,7 +14,7 @@ namespace Discord.Net.Converters | |||||
public override bool CanRead => true; | public override bool CanRead => true; | ||||
public override bool CanWrite => false; | public override bool CanWrite => false; | ||||
public ObjectOrIdConverter(JsonConverter innerConverter) | |||||
public UInt64EntityOrIdConverter(JsonConverter innerConverter) | |||||
{ | { | ||||
_innerConverter = innerConverter; | _innerConverter = innerConverter; | ||||
} | } | ||||
@@ -25,9 +25,14 @@ namespace Discord.Net.Converters | |||||
{ | { | ||||
case JsonToken.String: | case JsonToken.String: | ||||
case JsonToken.Integer: | case JsonToken.Integer: | ||||
return new ObjectOrId<T>(ulong.Parse(reader.ReadAsString())); | |||||
return new EntityOrId<T>(ulong.Parse(reader.ReadAsString())); | |||||
default: | default: | ||||
return new ObjectOrId<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 EntityOrId<T>(obj); | |||||
} | } | ||||
} | } | ||||