| @@ -18,10 +18,16 @@ namespace Discord.Rest | |||
| public DiscordRestClient(DiscordRestConfig config) : base(config) | |||
| { | |||
| _serializer = DiscordRestJsonSerializer.Global.CreateScope(); | |||
| _serializer.Error += ex => | |||
| if (config.LogLevel >= LogSeverity.Warning) | |||
| { | |||
| _restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | |||
| }; | |||
| _serializer.ModelError += (path, ex) | |||
| => _restLogger.WarningAsync($"Failed to deserialize {path}", ex).GetAwaiter().GetResult(); | |||
| } | |||
| if (config.LogLevel >= LogSeverity.Debug) | |||
| { | |||
| _serializer.UnmappedProperty += path | |||
| => _restLogger.DebugAsync($"Unmapped property: {path}"); | |||
| } | |||
| SetApiClient(new API.DiscordRestApiClient(config.RestClientProvider, DiscordConfig.UserAgent, _serializer, config.DefaultRetryMode)); | |||
| } | |||
| @@ -12,19 +12,19 @@ namespace Discord.Serialization.Json.Converters | |||
| _innerConverter = innerConverter; | |||
| } | |||
| public override EntityOrId<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override EntityOrId<T> Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| if (reader.ValueType == JsonValueType.Number) | |||
| return new EntityOrId<T>(reader.ParseUInt64()); | |||
| return new EntityOrId<T>(_innerConverter.Read(map, model, ref reader, false)); | |||
| return new EntityOrId<T>(_innerConverter.Read(serializer, modelMap, propMap, model, ref reader, false)); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, EntityOrId<T> value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, EntityOrId<T> value, string key) | |||
| { | |||
| if (value.Object != null) | |||
| _innerConverter.Write(map, model, ref writer, value.Object, key); | |||
| _innerConverter.Write(serializer, modelMap, propMap, model, ref writer, value.Object, key); | |||
| else | |||
| { | |||
| if (key != null) | |||
| @@ -5,7 +5,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| internal class ImagePropertyConverter : JsonPropertyConverter<API.Image> | |||
| { | |||
| public override API.Image Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override API.Image Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, 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 override void Write(PropertyMap map, object model, ref JsonWriter writer, API.Image value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, API.Image value, string key) | |||
| { | |||
| string str; | |||
| if (value.Stream != null) | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| internal class Int53PropertyConverter : JsonPropertyConverter<long> | |||
| { | |||
| public override long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override long Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number"); | |||
| return reader.ParseInt64(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, long value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, long value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -11,13 +11,13 @@ namespace Discord.Serialization.Json.Converters | |||
| _innerConverter = innerConverter; | |||
| } | |||
| 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 override Optional<T> Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| => new Optional<T>(_innerConverter.Read(serializer, modelMap, propMap, model, ref reader, isTopLevel)); | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, Optional<T> value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, Optional<T> value, string key) | |||
| { | |||
| if (value.IsSpecified) | |||
| _innerConverter.Write(map, model, ref writer, value.Value, key); | |||
| _innerConverter.Write(serializer, modelMap, propMap, model, ref writer, value.Value, key); | |||
| } | |||
| } | |||
| } | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| internal class UInt53PropertyConverter : JsonPropertyConverter<ulong> | |||
| { | |||
| public override ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override ulong Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number"); | |||
| return reader.ParseUInt64(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, ulong value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -41,6 +41,17 @@ namespace Discord.Serialization | |||
| } | |||
| public void Add(ReadOnlyBuffer<byte> key, TValue value) | |||
| { | |||
| if (!TryAdd(key, value)) | |||
| throw new ArgumentException("Duplicate key"); | |||
| } | |||
| public void Add(ReadOnlySpan<byte> key, TValue value) | |||
| { | |||
| if (!TryAdd(key, value)) | |||
| throw new ArgumentException("Duplicate key"); | |||
| } | |||
| public bool TryAdd(ReadOnlyBuffer<byte> key, TValue value) | |||
| { | |||
| int hashCode = GetKeyHashCode(key) & 0x7FFFFFFF; | |||
| int targetBucket = hashCode % _buckets.Length; | |||
| @@ -48,7 +59,7 @@ namespace Discord.Serialization | |||
| for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) | |||
| { | |||
| if (_entries[i].hashCode == hashCode && KeyEquals(_entries[i].key, key)) | |||
| throw new ArgumentException("Duplicate key", nameof(key)); | |||
| return false; | |||
| } | |||
| int index; | |||
| if (_freeCount > 0) | |||
| @@ -73,6 +84,43 @@ namespace Discord.Serialization | |||
| _entries[index].key = key; | |||
| _entries[index].value = value; | |||
| _buckets[targetBucket] = index; | |||
| return true; | |||
| } | |||
| //Duplicate code for perf reasons | |||
| public bool TryAdd(ReadOnlySpan<byte> key, TValue value) | |||
| { | |||
| int hashCode = GetKeyHashCode(key) & 0x7FFFFFFF; | |||
| int targetBucket = hashCode % _buckets.Length; | |||
| for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next) | |||
| { | |||
| if (_entries[i].hashCode == hashCode && KeyEquals(_entries[i].key, key)) | |||
| return false; | |||
| } | |||
| int index; | |||
| if (_freeCount > 0) | |||
| { | |||
| index = _freeList; | |||
| _freeList = _entries[index].next; | |||
| _freeCount--; | |||
| } | |||
| else | |||
| { | |||
| if (_count == _entries.Length) | |||
| { | |||
| Resize(); | |||
| targetBucket = hashCode % _buckets.Length; | |||
| } | |||
| index = _count; | |||
| _count++; | |||
| } | |||
| _entries[index].hashCode = hashCode; | |||
| _entries[index].next = _buckets[targetBucket]; | |||
| _entries[index].key = new ReadOnlyBuffer<byte>(key.ToArray()); | |||
| _entries[index].value = value; | |||
| _buckets[targetBucket] = index; | |||
| return true; | |||
| } | |||
| private void Resize() | |||
| { | |||
| @@ -94,13 +94,13 @@ namespace Discord.Serialization | |||
| public void AddSelector(Serializer serializer, string groupKey, Type keyType, object keyValue, Type converterType) | |||
| { | |||
| var group = CreateSelectorGroup(keyType, groupKey); | |||
| group.AddDynamicConverter(keyValue, BuildConverter(converterType, serializer)); | |||
| group.AddDynamicConverter(keyValue, BuildConverter(converterType, serializer )); | |||
| } | |||
| public object Get(Serializer serializer, Type type, PropertyInfo propInfo = null, bool throwOnNotFound = true) | |||
| { | |||
| //Check parent | |||
| object converter = _parent?.Get(serializer, type, propInfo, false); | |||
| var converter = _parent?.Get(serializer, type, propInfo, false); | |||
| if (converter != null) | |||
| return converter; | |||
| @@ -190,6 +190,7 @@ namespace Discord.Serialization | |||
| else | |||
| throw new SerializationException($"{converterType.Name} has an unsupported constructor"); | |||
| } | |||
| return constructor.Invoke(args); | |||
| } | |||
| @@ -7,6 +7,9 @@ | |||
| <TargetFrameworks>net45;netstandard1.1;netstandard1.3</TargetFrameworks> | |||
| <AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <Compile Include="..\Discord.Net.Core\Utils\ConcurrentHashSet.cs" Link="Utils\ConcurrentHashSet.cs" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="System.Buffers" Version="4.4.0" /> | |||
| <PackageReference Include="System.Collections.Immutable" Version="1.4.0" /> | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| _valueConverter = valueConverter; | |||
| } | |||
| public override Dictionary<string, TValue> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override Dictionary<string, TValue> Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartObject) | |||
| throw new SerializationException("Bad input, expected StartObject"); | |||
| @@ -26,20 +26,20 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected PropertyName"); | |||
| string key = reader.Value.ParseString(); | |||
| var value = _valueConverter.Read(map, model, ref reader, false); | |||
| var value = _valueConverter.Read(serializer, modelMap, propMap, 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) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, 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); | |||
| _valueConverter.Write(serializer, modelMap, propMap, model, ref writer, pair.Value, pair.Key); | |||
| writer.WriteObjectEnd(); | |||
| } | |||
| } | |||
| @@ -5,10 +5,10 @@ namespace Discord.Serialization.Json.Converters | |||
| //TODO: Only supports cases where the key arrives first | |||
| public class DynamicPropertyConverter : JsonPropertyConverter<object> | |||
| { | |||
| public override object Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override object Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (map.GetDynamicConverter(model, false) is IJsonPropertyReader<object> converter) | |||
| return converter.Read(map, model, ref reader, isTopLevel); | |||
| if (propMap.GetDynamicConverter(model, false) is IJsonPropertyReader<object> converter) | |||
| return converter.Read(serializer, modelMap, propMap, model, ref reader, isTopLevel); | |||
| else | |||
| { | |||
| JsonReaderUtils.Skip(ref reader); | |||
| @@ -16,7 +16,7 @@ namespace Discord.Serialization.Json.Converters | |||
| } | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, object value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, object value, string key) | |||
| { | |||
| if (value == null) | |||
| { | |||
| @@ -27,8 +27,8 @@ namespace Discord.Serialization.Json.Converters | |||
| } | |||
| else | |||
| { | |||
| var converter = (IJsonPropertyWriter)map.GetDynamicConverter(model, true); | |||
| converter.Write(map, model, ref writer, value, key); | |||
| var converter = (IJsonPropertyWriter)propMap.GetDynamicConverter(model, true); | |||
| converter.Write(serializer, modelMap, propMap, model, ref writer, value, key); | |||
| } | |||
| } | |||
| } | |||
| @@ -7,7 +7,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| private static readonly EnumMap<T> _map = EnumMap.For<T>(); | |||
| public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -15,7 +15,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return _map.FromInt64(reader.Value); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key) | |||
| { | |||
| long intVal = _map.ToInt64(value); | |||
| if (key != null) | |||
| @@ -30,7 +30,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| private static readonly EnumMap<T> _map = EnumMap.For<T>(); | |||
| public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -38,7 +38,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return _map.FromUInt64(reader.Value); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key) | |||
| { | |||
| ulong uintVal = _map.ToUInt64(value); | |||
| if (key != null) | |||
| @@ -53,7 +53,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| private static readonly EnumMap<T> _map = EnumMap.For<T>(); | |||
| public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -61,7 +61,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected String"); | |||
| return _map.FromKey(reader.Value); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key) | |||
| { | |||
| string strVal = _map.ToUtf16Key(value); | |||
| if (key != null) | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| _innerConverter = innerConverter; | |||
| } | |||
| public override T[] Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T[] Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray) | |||
| throw new SerializationException("Bad input, expected StartArray"); | |||
| @@ -22,19 +22,19 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| if (reader.TokenType == JsonTokenType.EndArray) | |||
| return list.ToArray(); | |||
| list.Add(_innerConverter.Read(map, model, ref reader, false)); | |||
| list.Add(_innerConverter.Read(serializer, modelMap, propMap, 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) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, 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); | |||
| _innerConverter.Write(serializer, modelMap, propMap, model, ref writer, value[i], null); | |||
| writer.WriteArrayEnd(); | |||
| } | |||
| } | |||
| @@ -48,7 +48,7 @@ namespace Discord.Serialization.Json.Converters | |||
| _innerConverter = innerConverter; | |||
| } | |||
| public override List<T> Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override List<T> Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if ((isTopLevel && !reader.Read()) || reader.TokenType != JsonTokenType.StartArray) | |||
| throw new SerializationException("Bad input, expected StartArray"); | |||
| @@ -58,19 +58,19 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| if (reader.TokenType == JsonTokenType.EndArray) | |||
| return list; | |||
| list.Add(_innerConverter.Read(map, model, ref reader, false)); | |||
| list.Add(_innerConverter.Read(serializer, modelMap, propMap, 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) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, 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); | |||
| _innerConverter.Write(serializer, modelMap, propMap, model, ref writer, value[i], null); | |||
| writer.WriteArrayEnd(); | |||
| } | |||
| } | |||
| @@ -12,19 +12,19 @@ namespace Discord.Serialization.Json.Converters | |||
| _innerConverter = innerConverter; | |||
| } | |||
| public override T? Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T? Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| if (reader.ValueType == JsonValueType.Null) | |||
| return null; | |||
| return _innerConverter.Read(map, model, ref reader, false); | |||
| return _innerConverter.Read(serializer, modelMap, propMap, model, ref reader, false); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, T? value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T? value, string key) | |||
| { | |||
| if (value.HasValue) | |||
| _innerConverter.Write(map, model, ref writer, value.Value, key); | |||
| _innerConverter.Write(serializer, modelMap, propMap, model, ref writer, value.Value, key); | |||
| else | |||
| { | |||
| if (key != null) | |||
| @@ -1,18 +1,19 @@ | |||
| using System.Text.Json; | |||
| using System; | |||
| using System.Text.Json; | |||
| namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class ObjectPropertyConverter<T> : JsonPropertyConverter<T> | |||
| where T : class, new() | |||
| { | |||
| private readonly ModelMap<T> _map; | |||
| private readonly ModelMap _map; | |||
| public ObjectPropertyConverter(Serializer serializer) | |||
| { | |||
| _map = serializer.MapModel<T>(); | |||
| } | |||
| public override T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| var subModel = new T(); | |||
| @@ -30,13 +31,19 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected PropertyName"); | |||
| if (_map.TryGetProperty(reader.Value, out var property)) | |||
| (property as IJsonPropertyMap<T>).Read(subModel, ref reader); | |||
| { | |||
| try { (property as IJsonPropertyMap<T>).Read(serializer, subModel, ref reader); } | |||
| catch (Exception ex) { RaiseModelError(serializer, property, ex); } | |||
| } | |||
| else | |||
| { | |||
| RaiseUnmappedProperty(serializer, _map, reader.Value); | |||
| JsonReaderUtils.Skip(ref reader); //Unknown property, skip | |||
| } | |||
| } | |||
| throw new SerializationException("Bad input, expected EndObject"); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key) | |||
| { | |||
| if (value == null) | |||
| { | |||
| @@ -51,8 +58,8 @@ namespace Discord.Serialization.Json.Converters | |||
| 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); | |||
| for (int i = 0; i < _map.Properties.Count; i++) | |||
| (_map.Properties[i] as IJsonPropertyMap<T>).Write(serializer, value, ref writer); | |||
| writer.WriteObjectEnd(); | |||
| } | |||
| } | |||
| @@ -5,7 +5,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class DateTimePropertyConverter : JsonPropertyConverter<DateTime> | |||
| { | |||
| public override DateTime Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override DateTime Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, 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 reader.ParseDateTime(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, DateTime value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, DateTime value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -24,7 +24,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class DateTimeOffsetPropertyConverter : JsonPropertyConverter<DateTimeOffset> | |||
| { | |||
| public override DateTimeOffset Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override DateTimeOffset Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -32,7 +32,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected String"); | |||
| return reader.ParseDateTimeOffset(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, DateTimeOffset value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, DateTimeOffset value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class SinglePropertyConverter : JsonPropertyConverter<float> | |||
| { | |||
| public override float Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override float Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseSingle(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, float value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, float value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -23,7 +23,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class DoublePropertyConverter : JsonPropertyConverter<double> | |||
| { | |||
| public override double Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override double Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -31,7 +31,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseDouble(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, double value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, double value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -42,7 +42,7 @@ namespace Discord.Serialization.Json.Converters | |||
| internal class DecimalPropertyConverter : JsonPropertyConverter<decimal> | |||
| { | |||
| public override decimal Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override decimal Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -50,7 +50,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseDecimal(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, decimal value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, decimal value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -5,7 +5,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class BooleanPropertyConverter : JsonPropertyConverter<bool> | |||
| { | |||
| public override bool Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override bool Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -16,7 +16,7 @@ namespace Discord.Serialization.Json.Converters | |||
| default: throw new SerializationException("Bad input, expected False or True"); | |||
| } | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, bool value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, bool value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -27,7 +27,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class GuidPropertyConverter : JsonPropertyConverter<Guid> | |||
| { | |||
| public override Guid Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override Guid Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -35,7 +35,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected String"); | |||
| return reader.ParseGuid(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, Guid value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, Guid value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class Int8PropertyConverter : JsonPropertyConverter<sbyte> | |||
| { | |||
| public override sbyte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override sbyte Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseInt8(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, sbyte value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, sbyte value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -23,7 +23,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class Int16PropertyConverter : JsonPropertyConverter<short> | |||
| { | |||
| public override short Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override short Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -31,7 +31,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseInt16(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, short value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, short value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -42,7 +42,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class Int32PropertyConverter : JsonPropertyConverter<int> | |||
| { | |||
| public override int Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override int Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -50,7 +50,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseInt32(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, int value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, int value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -61,7 +61,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class Int64PropertyConverter : JsonPropertyConverter<long> | |||
| { | |||
| public override long Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override long Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -69,7 +69,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseInt64(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, long value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, long value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| /*public class CharPropertyConverter : JsonPropertyConverter<char> | |||
| { | |||
| public override char Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override char Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected String"); | |||
| return reader.ParseChar(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, char value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, char value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -23,7 +23,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class StringPropertyConverter : JsonPropertyConverter<string> | |||
| { | |||
| public override string Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override string Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -33,7 +33,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected String"); | |||
| return reader.ParseString(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, string value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, string value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -4,7 +4,7 @@ namespace Discord.Serialization.Json.Converters | |||
| { | |||
| public class UInt8PropertyConverter : JsonPropertyConverter<byte> | |||
| { | |||
| public override byte Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override byte Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseUInt8(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, byte value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, byte value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -23,7 +23,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class UInt16PropertyConverter : JsonPropertyConverter<ushort> | |||
| { | |||
| public override ushort Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override ushort Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -31,7 +31,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseUInt16(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, ushort value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, ushort value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -42,7 +42,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class UInt32PropertyConverter : JsonPropertyConverter<uint> | |||
| { | |||
| public override uint Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override uint Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -50,7 +50,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseUInt32(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, uint value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, uint value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value); | |||
| @@ -61,7 +61,7 @@ namespace Discord.Serialization.Json.Converters | |||
| public class UInt64PropertyConverter : JsonPropertyConverter<ulong> | |||
| { | |||
| public override ulong Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public override ulong Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| if (isTopLevel) | |||
| reader.Read(); | |||
| @@ -69,7 +69,7 @@ namespace Discord.Serialization.Json.Converters | |||
| throw new SerializationException("Bad input, expected Number or String"); | |||
| return reader.ParseUInt64(); | |||
| } | |||
| public override void Write(PropertyMap map, object model, ref JsonWriter writer, ulong value, string key) | |||
| public override void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, ulong value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteAttribute(key, value.ToString()); | |||
| @@ -12,7 +12,7 @@ namespace Discord.Serialization.Json.Converters | |||
| _map = serializer.MapModel<T>(); | |||
| } | |||
| public T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel) | |||
| public T Read(ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel) | |||
| { | |||
| var subModel = new T(); | |||
| @@ -32,7 +32,7 @@ namespace Discord.Serialization.Json.Converters | |||
| } | |||
| throw new SerializationException("Bad input, expected EndObject"); | |||
| } | |||
| public void Write(PropertyMap map, object model, ref JsonWriter writer, T value, string key) | |||
| public void Write(ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key) | |||
| { | |||
| if (key != null) | |||
| writer.WriteObjectStart(key); | |||
| @@ -1,26 +0,0 @@ | |||
| using System.Text.Json; | |||
| namespace Discord.Serialization.Json | |||
| { | |||
| 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> | |||
| { | |||
| T Read(PropertyMap map, object model, ref JsonReader reader, bool isTopLevel); | |||
| } | |||
| public interface IJsonPropertyWriter<in T> | |||
| { | |||
| 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); | |||
| } | |||
| } | |||
| @@ -0,0 +1,34 @@ | |||
| using System; | |||
| using System.Text.Json; | |||
| namespace Discord.Serialization.Json | |||
| { | |||
| public abstract class JsonPropertyConverter<T> : IJsonPropertyReader<T>, IJsonPropertyWriter<T>, IJsonPropertyWriter | |||
| { | |||
| public abstract T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel); | |||
| public abstract void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key); | |||
| void IJsonPropertyWriter.Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, object value, string key) | |||
| => Write(serializer, modelMap, propMap, model, ref writer, (T)value, key); | |||
| protected void RaiseUnmappedProperty(Serializer serializer, ModelMap modelMap, ReadOnlyBuffer<byte> propertyKey) | |||
| => serializer.RaiseUnknownProperty(modelMap.Path, propertyKey); | |||
| protected void RaiseUnmappedProperty(Serializer serializer, ModelMap modelMap, ReadOnlySpan<byte> propertyKey) | |||
| => serializer.RaiseUnknownProperty(modelMap.Path, propertyKey); | |||
| protected void RaiseModelError(Serializer serializer, PropertyMap propMap, Exception ex) | |||
| => serializer.RaiseModelError(propMap.Path, ex); | |||
| } | |||
| public interface IJsonPropertyReader<out T> | |||
| { | |||
| T Read(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonReader reader, bool isTopLevel); | |||
| } | |||
| public interface IJsonPropertyWriter<in T> | |||
| { | |||
| void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, T value, string key); | |||
| } | |||
| public interface IJsonPropertyWriter | |||
| { | |||
| void Write(Serializer serializer, ModelMap modelMap, PropertyMap propMap, object model, ref JsonWriter writer, object value, string key); | |||
| } | |||
| } | |||
| @@ -9,36 +9,38 @@ namespace Discord.Serialization.Json | |||
| string Key { get; } | |||
| ReadOnlyBuffer<byte> Utf8Key { get; } | |||
| void Write(TModel model, ref JsonWriter writer); | |||
| void Read(TModel model, ref JsonReader reader); | |||
| void Write(Serializer serializer, TModel model, ref JsonWriter writer); | |||
| void Read(Serializer serializer, TModel model, ref JsonReader reader); | |||
| } | |||
| internal class JsonPropertyMap<TModel, TValue> : PropertyMap<TModel, TValue>, IJsonPropertyMap<TModel> | |||
| { | |||
| private readonly ModelMap _modelMap; | |||
| private readonly JsonPropertyConverter<TValue> _converter; | |||
| private readonly Func<TModel, TValue> _getFunc; | |||
| private readonly Action<TModel, TValue> _setFunc; | |||
| public JsonPropertyMap(Serializer serializer, PropertyInfo propInfo, JsonPropertyConverter<TValue> converter) | |||
| : base(serializer, propInfo) | |||
| public JsonPropertyMap(Serializer serializer, ModelMap modelMap, PropertyInfo propInfo, JsonPropertyConverter<TValue> converter) | |||
| : base(serializer, modelMap, propInfo) | |||
| { | |||
| _modelMap = modelMap; | |||
| _converter = converter; | |||
| _getFunc = propInfo.GetMethod.CreateDelegate(typeof(Func<TModel, TValue>)) as Func<TModel, TValue>; | |||
| _setFunc = propInfo.SetMethod.CreateDelegate(typeof(Action<TModel, TValue>)) as Action<TModel, TValue>; | |||
| } | |||
| public void Read(TModel model, ref JsonReader reader) | |||
| public void Read(Serializer serializer, TModel model, ref JsonReader reader) | |||
| { | |||
| var value = _converter.Read(this, model, ref reader, true); | |||
| var value = _converter.Read(serializer, _modelMap, this, model, ref reader, true); | |||
| _setFunc(model, value); | |||
| } | |||
| public void Write(TModel model, ref JsonWriter writer) | |||
| public void Write(Serializer serializer, TModel model, ref JsonWriter writer) | |||
| { | |||
| var value = _getFunc(model); | |||
| if (value == null && ExcludeNull) | |||
| return; | |||
| _converter.Write(this, model, ref writer, value, Key); | |||
| _converter.Write(serializer, _modelMap, this, model, ref writer, value, Key); | |||
| } | |||
| } | |||
| } | |||
| @@ -18,10 +18,10 @@ namespace Discord.Serialization.Json | |||
| where TConverter : JsonPropertyConverter<TValue> | |||
| => AddConverter(typeof(TValue), typeof(TConverter), condition); | |||
| protected override PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo) | |||
| protected override PropertyMap CreatePropertyMap<TModel, TValue>(ModelMap modelMap, PropertyInfo propInfo) | |||
| { | |||
| var converter = (JsonPropertyConverter<TValue>)GetConverter(typeof(TValue), propInfo); | |||
| return new JsonPropertyMap<TModel, TValue>(this, propInfo, converter); | |||
| return new JsonPropertyMap<TModel, TValue>(this, modelMap, propInfo, converter); | |||
| } | |||
| public TModel Read<TModel>(Utf8String str) | |||
| @@ -32,14 +32,16 @@ namespace Discord.Serialization.Json | |||
| if (!reader.Read()) | |||
| return default; | |||
| var converter = GetConverter(typeof(TModel)) as JsonPropertyConverter<TModel>; | |||
| return converter.Read(null, null, ref reader, false); | |||
| //Don't wrap this. We should throw an exception if we cant create the model. | |||
| return converter.Read(this, null, 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 JsonPropertyConverter<TModel>; | |||
| converter.Write(null, null, ref writer, model, null); | |||
| //Don't wrap this, always throw exceptions on Write. | |||
| converter.Write(this, null, null, null, ref writer, model, null); | |||
| } | |||
| } | |||
| } | |||
| @@ -1,22 +1,28 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| namespace Discord.Serialization | |||
| { | |||
| public class ModelMap<TModel> | |||
| where TModel : class, new() | |||
| public class ModelMap | |||
| { | |||
| private BufferDictionary<PropertyMap> _propDict; | |||
| private readonly List<PropertyMap> _propList; | |||
| private readonly BufferDictionary<PropertyMap> _propDict; | |||
| public string Path { get; } | |||
| public bool HasDynamics { get; } | |||
| public PropertyMap[] Properties { get; } | |||
| public IReadOnlyList<PropertyMap> Properties => _propList; | |||
| public ModelMap(Serializer serializer, TypeInfo type, List<PropertyMap> properties) | |||
| internal ModelMap(string path) | |||
| { | |||
| Properties = properties.ToArray(); | |||
| _propDict = new BufferDictionary<PropertyMap>(properties.ToDictionary(x => x.Utf8Key)); | |||
| Path = path; | |||
| _propList = new List<PropertyMap>(); | |||
| _propDict = new BufferDictionary<PropertyMap>(); | |||
| } | |||
| internal void AddProperty(PropertyMap propMap) | |||
| { | |||
| _propList.Add(propMap); | |||
| _propDict.Add(propMap.Utf8Key, propMap); | |||
| } | |||
| public bool TryGetProperty(ReadOnlyBuffer<byte> key, out PropertyMap value) | |||
| @@ -11,18 +11,19 @@ namespace Discord.Serialization | |||
| public string Key { get; } | |||
| public ReadOnlyBuffer<byte> Utf8Key { get; } | |||
| public string Name { get; } | |||
| public string Path { get; } | |||
| public bool ExcludeNull { get; } | |||
| public PropertyMap(Serializer serializer, PropertyInfo propInfo) | |||
| internal PropertyMap(Serializer serializer, PropertyInfo propInfo, ModelMap modelMap) | |||
| { | |||
| Name = propInfo.Name; | |||
| Path = $"{modelMap.Path}.{propInfo.Name}"; | |||
| var attr = propInfo.GetCustomAttribute<ModelPropertyAttribute>(); | |||
| Key = attr.Key ?? propInfo.Name; | |||
| Utf8Key = new ReadOnlyBuffer<byte>(new Utf8String(Key).Bytes.ToArray()); | |||
| ExcludeNull = attr.ExcludeNull; | |||
| } | |||
| public abstract object GetDynamicConverter(object model, bool throwOnMissing); | |||
| @@ -54,11 +55,10 @@ namespace Discord.Serialization | |||
| => _group?.GetDynamicConverter(_getWrappedSelectorFunc, model); | |||
| } | |||
| private readonly Delegate _getSelectorFunc, _getWrappedSelectorFunc; | |||
| private readonly IReadOnlyList<Selector> _selectors; | |||
| public PropertyMap(Serializer serializer, PropertyInfo propInfo) | |||
| : base(serializer, propInfo) | |||
| internal PropertyMap(Serializer serializer, ModelMap modelMap, PropertyInfo propInfo) | |||
| : base(serializer, propInfo, modelMap) | |||
| { | |||
| _selectors = propInfo.GetCustomAttributes<ModelSelectorAttribute>() | |||
| .Select(x => | |||
| @@ -4,18 +4,21 @@ using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| using System.Text.Formatting; | |||
| using System.Text.Utf8; | |||
| namespace Discord.Serialization | |||
| { | |||
| public abstract class Serializer | |||
| { | |||
| public event Action<Exception> Error; //TODO: Impl | |||
| public event Action<string, Exception> ModelError; | |||
| public event Action<string> UnmappedProperty; | |||
| private static readonly MethodInfo _createPropertyMapMethod | |||
| = typeof(Serializer).GetTypeInfo().GetDeclaredMethod(nameof(CreatePropertyMap)); | |||
| private readonly ConcurrentDictionary<Type, object> _maps; | |||
| private readonly ConverterCollection _converters; | |||
| private readonly ConcurrentHashSet<string> _unknownProps = new ConcurrentHashSet<string>(); | |||
| protected Serializer() | |||
| : this(null) { } | |||
| @@ -50,16 +53,18 @@ namespace Discord.Serialization | |||
| public ISelectorGroup GetSelectorGroup(Type keyType, string groupKey) | |||
| => _converters.GetSelectorGroup(keyType, groupKey); | |||
| protected internal ModelMap<TModel> MapModel<TModel>() | |||
| where TModel : class, new() | |||
| protected internal ModelMap MapModel<TModel>() | |||
| { | |||
| return _maps.GetOrAdd(typeof(TModel), _ => | |||
| { | |||
| var type = typeof(TModel).GetTypeInfo(); | |||
| var searchType = type; | |||
| var properties = new List<PropertyMap>(); | |||
| while (type != null) | |||
| var map = new ModelMap(type.Name); | |||
| while (searchType != null) | |||
| { | |||
| var propInfos = type.DeclaredProperties | |||
| var propInfos = searchType.DeclaredProperties | |||
| .Where(x => x.CanRead && x.CanWrite) | |||
| .ToArray(); | |||
| @@ -67,24 +72,68 @@ namespace Discord.Serialization | |||
| { | |||
| if (propInfos[i].GetCustomAttribute<ModelPropertyAttribute>() != null) | |||
| { | |||
| var propMap = MapProperty<TModel>(propInfos[i]); | |||
| properties.Add(propMap); | |||
| var propMap = MapProperty<TModel>(map, propInfos[i]); | |||
| map.AddProperty(propMap); | |||
| } | |||
| } | |||
| type = type.BaseType?.GetTypeInfo(); | |||
| searchType = searchType.BaseType?.GetTypeInfo(); | |||
| } | |||
| return new ModelMap<TModel>(this, type, properties); | |||
| }) as ModelMap<TModel>; | |||
| return map; | |||
| }) as ModelMap; | |||
| } | |||
| private PropertyMap MapProperty<TModel>(PropertyInfo propInfo) | |||
| => _createPropertyMapMethod.MakeGenericMethod(typeof(TModel), propInfo.PropertyType).Invoke(this, new object[] { propInfo }) as PropertyMap; | |||
| protected abstract PropertyMap CreatePropertyMap<TModel, TValue>(PropertyInfo propInfo); | |||
| private PropertyMap MapProperty<TModel>(ModelMap modelMap, PropertyInfo propInfo) | |||
| => _createPropertyMapMethod.MakeGenericMethod(typeof(TModel), propInfo.PropertyType).Invoke(this, new object[] { modelMap, propInfo }) as PropertyMap; | |||
| protected abstract PropertyMap CreatePropertyMap<TModel, TValue>(ModelMap modelMap, PropertyInfo propInfo); | |||
| public TModel Read<TModel>(ReadOnlyBuffer<byte> data) | |||
| => Read<TModel>(data.Span); | |||
| public abstract TModel Read<TModel>(ReadOnlySpan<byte> data); | |||
| public abstract void Write<TModel>(ArrayFormatter stream, TModel model); | |||
| internal void RaiseModelError(string path, Exception ex) | |||
| { | |||
| if (ModelError != null) | |||
| ModelError?.Invoke(path, ex); | |||
| } | |||
| internal void RaiseModelError(ModelMap modelMap, Exception ex) | |||
| { | |||
| if (ModelError != null) | |||
| ModelError?.Invoke(modelMap.Path, ex); | |||
| } | |||
| internal void RaiseModelError(PropertyMap propMap, Exception ex) | |||
| { | |||
| if (ModelError != null) | |||
| ModelError?.Invoke(propMap.Path, ex); | |||
| } | |||
| internal void RaiseUnmappedProperty(string model, string propertyMap) | |||
| { | |||
| if (UnmappedProperty != null) | |||
| { | |||
| string path = $"{model}.{propertyMap}"; | |||
| if (_unknownProps.TryAdd(path)) | |||
| UnmappedProperty?.Invoke(path); | |||
| } | |||
| } | |||
| internal void RaiseUnknownProperty(string model, ReadOnlyBuffer<byte> propertyMap) | |||
| { | |||
| if (UnmappedProperty != null) | |||
| { | |||
| string path = $"{model}.{new Utf8String(propertyMap.Span).ToString()}"; | |||
| if (_unknownProps.TryAdd(path)) | |||
| UnmappedProperty?.Invoke(path); | |||
| } | |||
| } | |||
| internal void RaiseUnknownProperty(string model, ReadOnlySpan<byte> propertyMap) | |||
| { | |||
| if (UnmappedProperty != null) | |||
| { | |||
| string path = $"{model}.{new Utf8String(propertyMap).ToString()}"; | |||
| if (_unknownProps.TryAdd(path)) | |||
| UnmappedProperty?.Invoke(path); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -68,10 +68,16 @@ namespace Discord.Audio | |||
| _audioLogger = Discord.LogManager.CreateLogger($"Audio #{clientId}"); | |||
| _serializer = DiscordVoiceJsonSerializer.Global.CreateScope(); | |||
| _serializer.Error += ex => | |||
| if (Discord.LogManager.Level >= LogSeverity.Warning) | |||
| { | |||
| _audioLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | |||
| }; | |||
| _serializer.ModelError += (path, ex) | |||
| => _audioLogger.WarningAsync($"Failed to deserialize {path}", ex).GetAwaiter().GetResult(); | |||
| } | |||
| if (Discord.LogManager.Level >= LogSeverity.Debug) | |||
| { | |||
| _serializer.UnmappedProperty += path | |||
| => _audioLogger.DebugAsync($"Unmapped property: {path}"); | |||
| } | |||
| ApiClient = new DiscordVoiceApiClient(guild.Id, Discord.WebSocketProvider, Discord.UdpSocketProvider, _serializer); | |||
| ApiClient.SentGatewayMessage += async opCode => await _audioLogger.DebugAsync($"Sent {opCode}").ConfigureAwait(false); | |||
| @@ -56,10 +56,16 @@ namespace Discord.WebSocket | |||
| _connectionGroupLock = new SemaphoreSlim(1, 1); | |||
| _serializer = DiscordSocketJsonSerializer.Global.CreateScope(); | |||
| _serializer.Error += ex => | |||
| if (config.LogLevel >= LogSeverity.Warning) | |||
| { | |||
| _restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | |||
| }; | |||
| _serializer.ModelError += (path, ex) | |||
| => _restLogger.WarningAsync($"Failed to deserialize {path}", ex).GetAwaiter().GetResult(); | |||
| } | |||
| if (config.LogLevel >= LogSeverity.Debug) | |||
| { | |||
| _serializer.UnmappedProperty += path | |||
| => _restLogger.DebugAsync($"Unmapped property: {path}"); | |||
| } | |||
| SetApiClient(new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordConfig.UserAgent, _serializer)); | |||
| @@ -88,10 +88,16 @@ namespace Discord.WebSocket | |||
| _gatewayLogger = LogManager.CreateLogger(ShardId == 0 && TotalShards == 1 ? "Gateway" : $"Shard #{ShardId}"); | |||
| _serializer = DiscordSocketJsonSerializer.Global.CreateScope(); | |||
| _serializer.Error += ex => | |||
| if (config.LogLevel >= LogSeverity.Warning) | |||
| { | |||
| _restLogger.WarningAsync("Serializer Error", ex).GetAwaiter().GetResult(); | |||
| }; | |||
| _serializer.ModelError += (path, ex) | |||
| => _gatewayLogger.WarningAsync($"Failed to deserialize {path}", ex).GetAwaiter().GetResult(); | |||
| } | |||
| if (config.LogLevel >= LogSeverity.Debug) | |||
| { | |||
| _serializer.UnmappedProperty += path | |||
| => _gatewayLogger.DebugAsync($"Unmapped property: {path}"); | |||
| } | |||
| SetApiClient(new API.DiscordSocketApiClient(config.RestClientProvider, config.WebSocketProvider, DiscordConfig.UserAgent, _serializer, config.GatewayHost, config.DefaultRetryMode)); | |||