@@ -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); | |||
@@ -42,13 +42,24 @@ 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; | |||
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); | |||
} | |||
@@ -8,6 +8,9 @@ | |||
<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" /> | |||
<PackageReference Include="System.Interactive.Async" Version="3.1.1" /> | |||
@@ -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)); | |||