我正在尝试使用自定义将一些JSON反序列化为各种子类 JsonConverter
JsonConverter
我几乎遵循了这一点。
我的抽象基类:
abstract class MenuItem { public String Title { get; set; } public String Contents { get; set; } public List<MenuItem> 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<String>()) { case SourceType.File: return item.ToObject<Menu.FileMenu>(); case SourceType.Folder: return item.ToObject<Menu.FolderMenu>(); case SourceType.Json: return item.ToObject<Menu.JsonMenu>(); case SourceType.RestGet: return item.ToObject<Menu.RestMenu>(); case SourceType.Rss: return item.ToObject<Menu.RssMenu>(); case SourceType.Text: return item.ToObject<Menu.TextMenu>(); case SourceType.Url: return item.ToObject<Menu.UrlMenu>(); default: throw new ArgumentException("Invalid source type"); } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
SourceType 只是一个包含一些字符串常量的静态类。
SourceType
JSON文件反序列化如下:
JsonConvert.DeserializeObject<MenuItem>(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" }
在我看来,反序列化嵌套对象有麻烦,我该如何解决?
首先,SourceType错过了您的json中的菜单项“ Submenus Test”。
其次,您不应该仅仅ToObject因为该Submenus属性而使用它,而应该以递归方式对其进行处理。
ToObject
Submenus
以下ReadJson将起作用:
ReadJson
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var sourceType = jObject["SourceType"].Value<string>(); 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; }