如何使用Newtonsoft Json.Net反序列化含接口列表的类型
解决Newtonsoft.Json反序列化接口集合的问题
我来帮你搞定这个反序列化的坑!先理清楚你的代码结构和遇到的问题:
你的类层级结构
public class ProxyBotsSnapshotLogEntryDetails : IBotsSnapshotLogEntryDetails { public ICollection<IBotSnapshot> Snapshots { get; set; } } public class ProxyBotSnapshot : IBotSnapshot { public string Name { get; set; } public ICollection<IBotSnapshotItem> States { get; set; } } public class ProxyBotSnapshotItem : IBotSnapshotItem { public int Count { get; set; } public IrcBotChannelStateEnum State { get; set; } }
对应的接口定义
public interface IBotsSnapshotLogEntryDetails { ICollection<IBotSnapshot> Snapshots { get; set; } } public interface IBotSnapshot { string Name { get; set; } ICollection<IBotSnapshotItem> States { get; set; } } public interface IBotSnapshotItem { int Count { get; set; } IrcBotChannelStateEnum State { get; set; } }
你遇到的核心问题是:执行var test = JsonConvert.DeserializeObject<ProxyBotsSnapshotLogEntryDetails>(entry.DetailsSerialized);时,Newtonsoft.Json没法直接把JSON数组转换成ICollection<IBotSnapshot>这类接口集合——毕竟接口不能被实例化,序列化器不知道该用哪个具体类来填充数据。
下面给你两种针对性的解决方案:
方案1:给接口集合属性添加特性指定具体实现
我们先写一个通用转换器,用来把接口类型映射到对应的具体实现类:
public class ConcreteTypeConverter<TConcrete> : JsonConverter { public override bool CanConvert(Type objectType) { // 判断目标类型是否为接口/抽象类,且TConcrete是它的实现类 return objectType.IsInterface || objectType.IsAbstract; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // 直接反序列化为指定的具体类型 return serializer.Deserialize<TConcrete>(reader); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // 序列化时直接处理即可 serializer.Serialize(writer, value); } }
然后修改你的类,给每个接口集合属性加上这个转换器特性:
public class ProxyBotsSnapshotLogEntryDetails : IBotsSnapshotLogEntryDetails { // 指定将ICollection<IBotSnapshot>反序列化为List<ProxyBotSnapshot> [JsonConverter(typeof(ConcreteTypeConverter<List<ProxyBotSnapshot>>))] public ICollection<IBotSnapshot> Snapshots { get; set; } } public class ProxyBotSnapshot : IBotSnapshot { public string Name { get; set; } // 指定将ICollection<IBotSnapshotItem>反序列化为List<ProxyBotSnapshotItem> [JsonConverter(typeof(ConcreteTypeConverter<List<ProxyBotSnapshotItem>>))] public ICollection<IBotSnapshotItem> States { get; set; } }
这样再执行反序列化代码,Newtonsoft就明确知道该实例化哪个具体类来填充接口集合了。
方案2:全局配置序列化设置(无需修改类定义)
如果你不想改动现有类的代码,可以在反序列化时通过JsonSerializerSettings全局指定接口对应的具体实现:
var serializerSettings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { ResolveContract = type => { // 为每个接口集合类型指定对应的具体实现 if (type == typeof(ICollection<IBotSnapshot>)) { return base.ResolveContract(typeof(List<ProxyBotSnapshot>)); } if (type == typeof(ICollection<IBotSnapshotItem>)) { return base.ResolveContract(typeof(List<ProxyBotSnapshotItem>)); } return base.ResolveContract(type); } } }; // 使用配置好的Settings进行反序列化 var test = JsonConvert.DeserializeObject<ProxyBotsSnapshotLogEntryDetails>(entry.DetailsSerialized, serializerSettings);
这种方式适合需要统一处理多个接口类型的场景,不用给每个属性加特性,灵活性更高。
内容的提问来源于stack exchange,提问作者Bruno
相关产品推荐
相关产品推荐

