2

我有以下界面:

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 类:

public class Sample
{
    public int X;
}

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

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

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

我怎样才能为这个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);
    }
}
4

1 回答 1

3

你的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()我们确定要反序列化的具体类型,方法是从传入的 中提取泛型参数,这是someobjectType所必需的,并使用相同的泛型参数构造具体类型。IInterface<M>MImplementation<M>

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

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

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

    有关在不使用转换器的情况下生成具体类的“默认”反序列化的其他选项,请参阅JSON.Net throws StackOverflowException when using [JsonConvert()]Call default JsonSerializer in a JsonConverter for certain value type arrays。建议构造具体类型的默认实例然后使用填充它的答案JsonSerializer.Populate()对您不起作用,因为您的具体类型没有默认构造函数。

Demo fiddles for DefaultSettings here和 for MyConverter+ NoConverter here

于 2021-01-17T18:45:26.003 回答