如何用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类:

public class Sample
{
public int X;
}

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

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

现在有一些遗留/外部代码(我不能改变),它试图使用JSON反序列化上述JSON对象。Net使用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);
}
}

您的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。当前线程的全局默认设置如下:

// 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支持从参数化构造函数反序列化,如所述:如何在不使用默认构造函数的情况下反序列化?。由于您的Implementation<M>具有满足所描述的需求的单个参数化构造函数,因此调用它来正确地反序列化您的具体类。

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

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

    用于生成"default"在不使用转换器的情况下对具体类进行反序列化,参见JSON。Net在使用[JsonConvert()]时抛出StackOverflowException,在JsonConverter中为某些值类型数组调用默认的JsonSerializer。建议构造一个具体类型的默认实例,然后使用JsonSerializer.Populate()填充它的答案将不适合您,因为您的具体类型没有默认构造函数。

DefaultSettings的Demo提琴,MyConverter+NoConverter的Demo提琴。

相关内容

  • 没有找到相关文章

最新更新