使用Json.Net失败的多态JSON反序列化
我正在尝试使用自定义JsonConverter
将一些JSON反序列化为各种子类
我几乎遵循了这一点。
我的抽象基类:
abstract class MenuItem { public String Title { get; set; } public String Contents { get; set; } public List Submenus { get; set; } public String Source { get; set; } public String SourceType { get; set; } public abstract void DisplayContents(); }
而我派生的JsonConverter
:
class MenuItemConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(MenuItem).IsAssignableFrom(objectType); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject item = JObject.Load(reader); switch (item["SourceType"].Value()) { case SourceType.File: return item.ToObject
SourceType
只是一个包含一些字符串常量的静态类。
JSON文件反序列化如下:
JsonConvert.DeserializeObject(File.ReadAllText(menuPath), new MenuItemConverter());
现在,我的问题是每当我运行代码时,我都会收到以下错误:
An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code Additional information: Could not create an instance of type ConsoleMenu.Model.MenuItem. Type is an interface or abstract class and cannot be instantiated. Path 'Submenus[0].Title', line 5, position 21.
有问题的Json文件如下所示:
{ "Title": "Main Menu", "Submenus": [ { "Title": "Submenu 1", "Contents": "This is an example of the first sub-menu", "SourceType": "Text" }, { "Title": "Submenu 2", "Contents": "This is the second sub-menu", "SourceType": "Text" }, { "Title": "GitHub System Status", "Contents": "{\"status\":\"ERROR\",\"body\":\"If you see this, the data failed to load\"}", "Source": "https://status.github.com/api/last-message.json", "SourceType": "RestGet" }, { "Title": "TF2 Blog RSS", "Contents": "If you see this message, an error has occurred", "Source": "http://www.teamfortress.com/rss.xml", "SourceType": "Rss" }, { "Title": "Submenus Test", "Contents": "Testing the submenu functionality", "Submenus": [ { "Title": "Submenu 1", "Contents": "This is an example of the first sub-menu", "SourceType": "Text" }, { "Title": "Submenu 2", "Contents": "This is the second sub-menu", "SourceType": "Text" } ] } ], "SourceType": "Text" }
在我看来,它无法反序列化嵌套对象,我该如何解决这个问题呢?
首先,json中的菜单项“Submenus Test”错过了SourceType
。
其次,你不应该简单地使用ToObject
因为ToObject
属性应该递归处理。
以下ReadJson
将起作用:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var sourceType = jObject["SourceType"].Value(); object target = null; switch (sourceType) { case SourceType.File: target = new FileMenu(); break; case SourceType.Folder: target = new FolderMenu(); break; case SourceType.Json: target = new JsonMenu(); break; case SourceType.RestGet: target = new RestMenu(); break; case SourceType.Rss: target = new RssMenu(); break; case SourceType.Text: target = new TextMenu(); break; case SourceType.Url: target = new UrlMenu(); break; default: throw new ArgumentException("Invalid source type"); } serializer.Populate(jObject.CreateReader(), target); return target; }
您收到错误的原因是因为您的MenuItem
类被标记为abstract
。 我猜你这样做是为了在inheritance的类中强制执行DisplayContents()
方法。
与Mouhong Lin建议的一样,允许读取Json的另一种方法是为MenuItem结构创建一个基本Interface
,让你的MenuItem
类用DisplayContents()
方法的基本版本实现接口,将其标记为虚拟然后在inheritance的子类中覆盖它。
这种方法将确保您在调用DisplayContents()
时始终显示一些内容,并删除您获得的错误。
类和接口的非常粗略和简化版本:
public interface IMenuItem { String Title { get; set; } String Contents { get; set; } List