mirror of
https://github.com/vknet/vk.git
synced 2026-10-19 01:40:11 +00:00
refactor: remove obsolete StreamingContext from enum deserialization
Pass DeserializationErrorHandler directly to TolerantStringEnumConverter through a contract resolver instead of using StreamingContext. Remove the global JsonConvert.DefaultSettings mutation and obsolete StreamingContext helper classes. Preserve guidance for reporting unknown VK API enum values.
This commit is contained in:
parent
3d9db2caba
commit
761c2b3d19
5 changed files with 43 additions and 119 deletions
|
|
@ -1,27 +0,0 @@
|
|||
using System;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace VkNet.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Интерфейс словаря контекста
|
||||
/// </summary>
|
||||
public interface IStreamingContextTypeDataDictionary
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавить данные
|
||||
/// </summary>
|
||||
/// <param name="type">Тип</param>
|
||||
/// <param name="data">Данные</param>
|
||||
public void AddData(Type type, [CanBeNull] object data);
|
||||
|
||||
/// <summary>
|
||||
/// Попытка получения данных
|
||||
/// </summary>
|
||||
/// <param name="type">Тип</param>
|
||||
/// <param name="data">Данные</param>
|
||||
/// <returns>
|
||||
/// Успешность получения данных
|
||||
/// </returns>
|
||||
public bool TryGetData(Type type, [CanBeNull] out object data);
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace VkNet.Infrastructure;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для кэширования данных в StreamingContext
|
||||
/// </summary>
|
||||
public static class StreamingContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Добавить данные в контекст
|
||||
/// </summary>
|
||||
/// <param name="context">Контекст данных</param>
|
||||
/// <param name="type">Тип</param>
|
||||
/// <param name="data">Данные</param>
|
||||
/// <returns>
|
||||
/// Контекст
|
||||
/// </returns>
|
||||
public static StreamingContext AddTypeData(this StreamingContext context, Type type, [CanBeNull] object data)
|
||||
{
|
||||
var dictionary = context.Context switch
|
||||
{
|
||||
null => new StreamingContextTypeDataDictionary(),
|
||||
IStreamingContextTypeDataDictionary d => d,
|
||||
var _ => throw new InvalidOperationException($"context.Context is already populated with {context.Context}")
|
||||
};
|
||||
|
||||
dictionary.AddData(type, data);
|
||||
|
||||
return new(context.State, dictionary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Попытка получения данных из контекста
|
||||
/// </summary>
|
||||
/// <param name="context">Контекст</param>
|
||||
/// <param name="type">Тип</param>
|
||||
/// <param name="data">Данные</param>
|
||||
/// <returns>
|
||||
/// Успешность получения данных
|
||||
/// </returns>
|
||||
public static bool TryGetTypeData(this StreamingContext context, Type type, [CanBeNull] out object data)
|
||||
{
|
||||
if (context.Context is IStreamingContextTypeDataDictionary dictionary)
|
||||
{
|
||||
return dictionary.TryGetData(type, out data);
|
||||
}
|
||||
|
||||
data = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VkNet.Infrastructure;
|
||||
|
||||
/// <inheritdoc cref="IStreamingContextTypeDataDictionary" />
|
||||
internal class StreamingContextTypeDataDictionary : IStreamingContextTypeDataDictionary
|
||||
{
|
||||
private readonly Dictionary<Type, object> _dictionary = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddData(Type type, object data) => _dictionary.Add(type, data);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetData(Type type, out object data) => _dictionary.TryGetValue(type, out data);
|
||||
}
|
||||
|
|
@ -2,16 +2,25 @@
|
|||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using VkNet.Infrastructure;
|
||||
|
||||
namespace VkNet.Utils.JsonConverter;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class TolerantStringEnumConverter : StringEnumConverter
|
||||
{
|
||||
private readonly bool? _ignoreUnknownValues;
|
||||
|
||||
/// <inheritdoc />
|
||||
public TolerantStringEnumConverter() => NamingStrategy = new SnakeCaseNamingStrategy();
|
||||
|
||||
/// <summary>
|
||||
/// Создает конвертер с указанным поведением для неизвестных значений enum.
|
||||
/// </summary>
|
||||
/// <param name="ignoreUnknownValues">
|
||||
/// Следует ли заменить неизвестное значение nullable enum на <see langword="null"/>.
|
||||
/// </param>
|
||||
public TolerantStringEnumConverter(bool? ignoreUnknownValues) : this() => _ignoreUnknownValues = ignoreUnknownValues;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
|
|
@ -19,15 +28,12 @@ public class TolerantStringEnumConverter : StringEnumConverter
|
|||
{
|
||||
return base.ReadJson(reader, objectType, existingValue, serializer);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (System.Exception)
|
||||
{
|
||||
var parameter = serializer.Context.TryGetTypeData(typeof(TolerantStringEnumConverter), out var s)
|
||||
? (bool?) s
|
||||
: null;
|
||||
|
||||
if (IsNullableType(objectType) && parameter is true)
|
||||
if (IsNullableType(objectType) && _ignoreUnknownValues is true)
|
||||
{
|
||||
//_logger.LogError($"\nПОЖАЛУЙСТА ОТКРОЙТЕ ISSUE: {$"https://github.com/vknet/vk/issues/new?title=Add%20new%20value%20to%20{objectType.GenericTypeArguments.FirstOrDefault()}&body={reader.Value}"}\n" + e.Message);
|
||||
// Неизвестное значение может означать, что VK API добавил новый элемент enum.
|
||||
// Создайте Issue: https://github.com/vknet/vk/issues/new, чтобы добавить его в соответствующий enum библиотеки.
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -36,4 +42,30 @@ public class TolerantStringEnumConverter : StringEnumConverter
|
|||
}
|
||||
|
||||
private static bool IsNullableType(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Настраивает конвертеры enum, заданные через <see cref="JsonConverterAttribute"/>.
|
||||
/// </summary>
|
||||
internal sealed class TolerantStringEnumContractResolver(bool? ignoreUnknownValues) : DefaultContractResolver
|
||||
{
|
||||
protected override JsonContract CreateContract(Type objectType)
|
||||
{
|
||||
var contract = base.CreateContract(objectType);
|
||||
contract.Converter = ReplaceTolerantConverter(contract.Converter);
|
||||
|
||||
return contract;
|
||||
}
|
||||
|
||||
protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
var property = base.CreateProperty(member, memberSerialization);
|
||||
property.Converter = ReplaceTolerantConverter(property.Converter);
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
private Newtonsoft.Json.JsonConverter ReplaceTolerantConverter(Newtonsoft.Json.JsonConverter converter) => converter is TolerantStringEnumConverter
|
||||
? new TolerantStringEnumConverter(ignoreUnknownValues)
|
||||
: converter;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -271,22 +270,13 @@ public class VkApi : IVkApi
|
|||
{
|
||||
var answer = CallBase(methodName, parameters, skipAuthorization);
|
||||
|
||||
var context = new StreamingContext(StreamingContextStates.All)
|
||||
.AddTypeData(typeof(TolerantStringEnumConverter), DeserializationErrorHandler);
|
||||
|
||||
JsonConvert.DefaultSettings = () => new()
|
||||
{
|
||||
Context = context
|
||||
};
|
||||
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
Converters = [],
|
||||
ContractResolver = new DefaultContractResolver
|
||||
ContractResolver = new TolerantStringEnumContractResolver(DeserializationErrorHandler)
|
||||
{
|
||||
NamingStrategy = new SnakeCaseNamingStrategy()
|
||||
},
|
||||
Context = context,
|
||||
MaxDepth = null,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
};
|
||||
|
|
@ -1062,4 +1052,4 @@ public class VkApi : IVkApi
|
|||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue