1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-07 16:06:15 +00:00

Fixed System.Text.Json ArrayConverter not passing serializer options to nested deserialization, fixed creating new serializer options each time a JsonConverter attribute is encountered

This commit is contained in:
Jkorf 2024-11-01 10:34:07 +01:00
parent 17f1560310
commit 1aed9f0c67
2 changed files with 27 additions and 10 deletions

View File

@ -38,7 +38,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
private class ArrayConverterInner<T> : JsonConverter<T> private class ArrayConverterInner<T> : JsonConverter<T>
{ {
private static readonly ConcurrentDictionary<Type, List<ArrayPropertyInfo>> _typeAttributesCache = new ConcurrentDictionary<Type, List<ArrayPropertyInfo>>(); private static readonly ConcurrentDictionary<Type, List<ArrayPropertyInfo>> _typeAttributesCache = new ConcurrentDictionary<Type, List<ArrayPropertyInfo>>();
private static readonly ConcurrentDictionary<Type, JsonSerializerOptions> _converterOptionsCache = new ConcurrentDictionary<Type, JsonSerializerOptions>();
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{ {
@ -108,7 +108,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return default; return default;
var result = Activator.CreateInstance(typeToConvert); var result = Activator.CreateInstance(typeToConvert);
return (T)ParseObject(ref reader, result, typeToConvert); return (T)ParseObject(ref reader, result, typeToConvert, options);
} }
private static bool IsSimple(Type type) private static bool IsSimple(Type type)
@ -148,7 +148,7 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
return attributes; return attributes;
} }
private static object ParseObject(ref Utf8JsonReader reader, object result, Type objectType) private static object ParseObject(ref Utf8JsonReader reader, object result, Type objectType, JsonSerializerOptions options)
{ {
if (reader.TokenType != JsonTokenType.StartArray) if (reader.TokenType != JsonTokenType.StartArray)
throw new Exception("Not an array"); throw new Exception("Not an array");
@ -175,15 +175,24 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
object? value = null; object? value = null;
if (attribute.JsonConverterType != null) if (attribute.JsonConverterType != null)
{ {
// Has JsonConverter attribute if (!_converterOptionsCache.TryGetValue(attribute.JsonConverterType, out var newOptions))
var options = new JsonSerializerOptions(); {
options.Converters.Add((JsonConverter)Activator.CreateInstance(attribute.JsonConverterType)); var converter = (JsonConverter)Activator.CreateInstance(attribute.JsonConverterType);
value = JsonDocument.ParseValue(ref reader).Deserialize(targetType, options); newOptions = new JsonSerializerOptions
{
NumberHandling = SerializerOptions.WithConverters.NumberHandling,
PropertyNameCaseInsensitive = SerializerOptions.WithConverters.PropertyNameCaseInsensitive,
Converters = { converter },
};
_converterOptionsCache.TryAdd(attribute.JsonConverterType, newOptions);
}
value = JsonDocument.ParseValue(ref reader).Deserialize(targetType, newOptions);
} }
else if (attribute.DefaultDeserialization) else if (attribute.DefaultDeserialization)
{ {
// Use default deserialization // Use default deserialization
value = JsonDocument.ParseValue(ref reader).Deserialize(targetType); value = JsonDocument.ParseValue(ref reader).Deserialize(targetType, options);
} }
else else
{ {
@ -194,11 +203,14 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
JsonTokenType.True => true, JsonTokenType.True => true,
JsonTokenType.String => reader.GetString(), JsonTokenType.String => reader.GetString(),
JsonTokenType.Number => reader.GetDecimal(), JsonTokenType.Number => reader.GetDecimal(),
JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, attribute.TargetType), JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, attribute.TargetType, options),
_ => throw new NotImplementedException($"Array deserialization of type {reader.TokenType} not supported"), _ => throw new NotImplementedException($"Array deserialization of type {reader.TokenType} not supported"),
}; };
} }
if (targetType.IsAssignableFrom(value?.GetType()))
attribute.PropertyInfo.SetValue(result, value == null ? null : value);
else
attribute.PropertyInfo.SetValue(result, value == null ? null : Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture)); attribute.PropertyInfo.SetValue(result, value == null ? null : Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture));
} }

View File

@ -50,6 +50,11 @@ namespace CryptoExchange.Net.Converters.SystemTextJson
var info = $"Deserialize JsonException: {ex.Message}, Path: {ex.Path}, LineNumber: {ex.LineNumber}, LinePosition: {ex.BytePositionInLine}"; var info = $"Deserialize JsonException: {ex.Message}, Path: {ex.Path}, LineNumber: {ex.LineNumber}, LinePosition: {ex.BytePositionInLine}";
return new CallResult<object>(new DeserializeError(info, OriginalDataAvailable ? GetOriginalString() : "[Data only available when OutputOriginal = true in client options]")); return new CallResult<object>(new DeserializeError(info, OriginalDataAvailable ? GetOriginalString() : "[Data only available when OutputOriginal = true in client options]"));
} }
catch (Exception ex)
{
var info = $"Deserialize unknown Exception: {ex.Message}";
return new CallResult<object>(new DeserializeError(info, OriginalDataAvailable ? GetOriginalString() : "[Data only available when OutputOriginal = true in client options]"));
}
} }
/// <inheritdoc /> /// <inheritdoc />