[英]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)
如何为这个处理其通用参数M
的IInterface
接口编写 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()
填充它的答案对您不起作用,因为您的具体类型没有默认构造函数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.