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