Json.Net 自定义转换器的自递归

Json.Net Self Recurse on Custom Converter

问题的细节可能有点长,所以我会在开头简短地描述一下:如何强制Json.Net使用它的默认对象序列化器(或忽略换句话说,一个特定的自定义转换器),但在反序列化对象时仍将设置保留在 JsonSerializer 中?
为我糟糕的英语道歉,描述可能有点模棱两可和令人困惑。我会用我的详细场景来解释。
在处理 HTTP 响应时,我们有时会遇到这样的场景,即一个对象是其父对象的唯一子对象,使得父对象在某种程度上成为无意义的 object wrapper。在一些糟糕的设计中,可能会有多层这样的包装器。如果我们想要在不自定义的情况下正确反序列化这样的 JSON,我们必须按照结构来定义那些包装器 classes,这绝对是毫无意义和烦人的,因此我想出了创建一个通用的想法-目的 ObjectWrapperConverter。这是代码:

public class ObjectWrapperConverter<T> : ObjectWrapperConverterBase<T> {
    public ObjectWrapperConverter(string propertyName) : this(propertyName, Array.Empty<JsonConverter>()) { }

    public ObjectWrapperConverter(string propertyName, params JsonConverter[] converters) {
        PropertyName = propertyName;
        Converters = converters;
    }

    public override string PropertyName { get; }

    public override JsonConverter[] Converters { get; }
}

public abstract class ObjectWrapperConverterBase<T> : JsonConverter<T> {
    public abstract string PropertyName { get; }

    public abstract JsonConverter[] Converters { get; }

    public sealed override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) {
        writer.WriteStartObject();
        writer.WritePropertyName(PropertyName);
        serializer.Converters.AddRange(Converters);
        writer.WriteValue(value, serializer);
        serializer.Converters.RemoveRange(Converters);
        writer.WriteEndObject();
    }

    public sealed override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) {
        var token = JToken.Load(reader);
        if (token.Type != JTokenType.Object)
            throw new JTokenTypeException(token, JTokenType.Object);
        var obj = token as JObject;
        var prop = obj!.Property(PropertyName);
        if (prop is null)
            throw new JTokenException(token, $"Property \"{PropertyName}\" not found");
        serializer.Converters.AddRange(Converters);
        var result = prop.Value.ToObject<T>(serializer);//BUG: recurse when applying JsonConverterAttribute to a class
        serializer.Converters.RemoveRange(Converters);
        return result;
    }
}

当我将 JsonConverterAttribute 放在属性和字段上时,它工作正常。但是在注解class的时候出现问题:反序列化过程陷入了递归循环
我调试了 Json.Net 框架,并意识到当为 class 指定自定义转换器时,Json.Net 将始终使用此转换器来处理此 class 的序列化,除非更高 -优先级属性(如放置在属性上的 JsonConverterAttribute)被注释。因此,在我的转换器中,我放置注释的行最终将导致递归。 如果你了解了这个转换器的用途,很容易发现这个转换器只是一个中间件:添加或删除包装对象,并继续原来的序列化过程。
那么,如何才能继续“原始”的序列化过程,而不是再次陷入转换器本身呢?

我对 Newtonsoft.Json 框架进行了更深入的探索,发现了它如何在没有转换器的情况下序列化和反序列化对象。
Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue
因为这些 类 和方法是内部的,所以我必须使用反射来调用它们。因此,我将其封装为两个扩展方法:

public static class NewtonsoftExtensions{
    private static Type JsonSerializerInternalReader { get; } = typeof(JsonSerializer).Assembly.GetType("Newtonsoft.Json.Serialization.JsonSerializerInternalReader");

    private static Type JsonSerializerInternalWriter { get; } = typeof(JsonSerializer).Assembly.GetType("Newtonsoft.Json.Serialization.JsonSerializerInternalWriter");

    private static MethodInfo CreateValueInternal { get; } = JsonSerializerInternalReader.GetMethod("CreateValueInternal", BindingFlags.NonPublic | BindingFlags.Instance);

    private static MethodInfo SerializeValue { get; } = JsonSerializerInternalWriter.GetMethod("SerializeValue", BindingFlags.NonPublic | BindingFlags.Instance);

    public object DeserializeWithoutContractConverter(this JsonSerializer serializer, JsonReader reader, Type objectType) {
        var contract = serializer.ContractResolver.ResolveContract(objectType);
        var converter = contract.Converter;
        contract.Converter = null;
        object internalReader = Activator.CreateInstance(JsonSerializerInternalReader, serializer);
        object result = CreateValueInternal.Invoke(internalReader, reader, objectType, contract, null, null, null, null);
        contract.Converter = converter; //DefaultContractResolver caches the contract of each type, thus we need to restore the original converter for future use
        return result;
    }

    public void SerializeWithoutContractConverter(this JsonSerializer serializer, JsonWriter writer, object value) {
        var contract = serializer.ContractResolver.ResolveContract(value.GetType());
        var converter = contract.Converter;
        contract.Converter = null;
        object internalWriter = Activator.CreateInstance(JsonSerializerInternalWriter, serializer);
        SerializeValue.Invoke(internalWriter, writer, value, contract, null, null, null);
        contract.Converter = converter;
    }
}

使用反射调用内部方法有风险,不推荐使用,但与中的其他答案相比,这种方法会充分利用序列化程序设置。如果转换器是通用的,就像我正在尝试实现的 ObjectWrapperConverter 一样,这将导致最少的意外结果,因为 Newtonsoft.Json 有大量的设置供用户自定义行为。