繁体   English   中英

如何使用 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;
}

这是我从 javascript 客户端传递的示例 JSON:

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

现在有一些遗留/外部代码(我无法更改)试图反序列化上述 JSON object 使用 Json.Net 使用DeserializeObject<IInterface<Sample>>(js_object_string)

如何为这个处理其通用参数MIInterface接口编写 JsonConverter 。 Internet 上的大多数解决方案仅适用于编译时已知的类型。

我尝试了下面的代码(我不完全理解),但外部代码认为反序列化的 object 不是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 的当前线程的 全局默认设置中,如下所示:

// 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如何使用 Json.Net 将通用接口反序列化为通用具体类型? Implementation<M>以避免堆栈溢出异常:

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

笔记:

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

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

  • Json.NET 支持从参数化构造函数进行反序列化,如JSON.net 中所述:如何在不使用默认构造函数的情况下进行反序列化? . 由于您的Implementation<M>具有满足所述要求的单个参数化构造函数,因此调用它以正确反序列化您的具体 class。

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

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

    For other options to generate a "default" deserialization of the concrete class without using a converter, see JSON.Net throws StackOverflowException when using [JsonConvert()] or Call default JsonSerializer in a JsonConverter for certain value type arrays . 建议构造具体类型的默认实例然后使用JsonSerializer.Populate()填充它的答案对您不起作用,因为您的具体类型没有默认构造函数。

此处DefaultSettings演示小提琴,此处MyConverter + NoConverter

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM