如何使用 Json.Net 将泛型接口反序列化为泛型具体类型?

How to deserialize generic interface to generic concrete type with Json.Net?

我有以下界面:

public interface IInterface<out M>
{
    M Message { get; }
    string Str { get; }
}

及其实现:

public class Implementation<M> : IInterface<M>
{
    public M Message;
    public string Str;

    public Implementation(M message, string str)
    {
        Message = message;
        Str = str;
    }

    M IInterface<M>.Message => this.Message;
    string IInterface<M>.Str => this.Str;
}

这是一个示例 M class:

public class Sample
{
    public int X;
}

这是 JSON 我从 javascript 客户那里传来的样本:

{ "Message" : { "X": 100 }, "Str" : "abc" }

现在有一些 legacy/external 代码(我无法更改)尝试使用 Json.Net 使用 DeserializeObject<IInterface<Sample>>(js_object_string) 反序列化上述 JSON 对象。

我如何为这个 IInterface 接口编写一个 JsonConverter 来处理它的通用参数 M。互联网上的大多数解决方案仅适用于编译时已知的类型。

我试过下面的代码(我不完全理解)但是外部代码不认为反序列化的对象是 IInterface.

static class ReflectionHelper
{
    public static IInterface<T> Get<T>()
    {
        var x = JsonConvert.DeserializeObject<T>(str);
        IInterface<T> y = new Implementation<T>(x, "xyz");
        return y;
    }
}

class MyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
       return (objectType == typeof(IInterface<>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
       var w = Newtonsoft.Json.Linq.JObject.Load(reader);
       var x = typeof(ReflectionHelper).GetMethod(nameof(ReflectionHelper.Get)).MakeGenericMethod(objectType.GetGenericArguments()[0]).Invoke(null, new object[] {  });

       return x;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
       serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // otherwise I get a circular dependency error.
       serializer.Serialize(writer, value);
    }
}

你的MyConverter可以这样写:

public class MyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) =>
       objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(IInterface<>);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (!CanConvert(objectType)) // For safety.
            throw new ArgumentException(string.Format("Invalid type {0}", objectType));
        var concreteType = typeof(Implementation<>).MakeGenericType(objectType.GetGenericArguments());
        return serializer.Deserialize(reader, concreteType);
    }

    public override bool CanWrite => false;

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

然后添加到Converters进行序列化和反序列化如下:

var settings = new JsonSerializerSettings
{
    Converters = { new MyConverter() },
};
var root = JsonConvert.DeserializeObject<IInterface<Sample>>(js_object_string, settings);

如果你真的不能改变对 DeserializeObject<IInterface<Sample>>(js_object_string) 的调用 ,你可以将你的转换器添加到 Json.NET 的 global default settings对于当前线程,像这样:

// Set up Json.NET's global default settings to include MyConverter
JsonConvert.DefaultSettings = () => new JsonSerializerSettings
    {
        Converters = { new MyConverter() },
    };

// And then later, deserialize to IInterface<Sample> via a call that cannot be changed AT ALL:
var root = JsonConvert.DeserializeObject<IInterface<Sample>>(js_object_string);

或者,您可以将 MyConverter 直接应用于 IInterface<out M>,如下所示:

[JsonConverter(typeof(MyConverter))]
public interface IInterface<out M>
{

但如果这样做,您必须将 NoConverter to 应用到 Implementation<M> 以避免堆栈溢出异常:

[JsonConverter(typeof(NoConverter))]
public class Implementation<M> : IInterface<M>
{

备注:

  • 通过覆盖 JsonConverter.CanWrite 并返回 false 我们避免了实现 WriteJson().

    的需要
  • ReadJson()中我们通过从传入的objectType中提取通用参数来确定要反序列化的具体类型,对于某些M,并使用相同的泛型参数构造具体类型 Implementation<M>

  • Json.NET 支持从参数化构造函数反序列化,如 JSON.net: how to deserialize without using the default constructor? 中所述。由于您的 Implementation<M> 具有满足所述要求的单个参数化构造函数,因此调用它以正确反序列化您的具体 class。

  • DefaultSettings applies to all calls to JsonConvert 在整个应用程序的所有线程中,因此您应该确定修改这些设置是否适合您的应用程序。

  • NoConverter 必须应用于 Implementation<M>,因为在没有自己的转换器的情况下,它将从 IInterface<out M> 继承 MyConverter这随后会在反序列化具体类型时导致对 MyConverter.ReadJson() 的递归调用,从而导致堆栈溢出或循环引用异常。 (这个你可以自己调试确认)

    有关在不使用转换器的情况下生成具体 class 的“默认”反序列化的其他选项,请参阅 or . Answers that suggest constructing a default instance of the concrete type and then populating it using JsonSerializer.Populate() 对您不起作用,因为您的具体类型不适用有一个默认的构造函数。

DefaultSettings here, and for MyConverter + NoConverter here.

的演示小提琴