2015-03-18 20 views
8

Próbuję deserializowania niektóre JSON do różnych podklas użyciu niestandardowego JsonConverterpolimorficzne JSON deserializacji braku użyciu Json.NET

Śledziłem this niemal do punktu.

Moje streszczenie base-klasa:

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(); 
} 

A moja pochodzi 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 jest tylko statyczne klasy gospodarstwa niektóre stałe ciągów znaków.

Plik JSON jest rozszeregować tak:

JsonConvert.DeserializeObject<MenuItem>(File.ReadAllText(menuPath), new MenuItemConverter()); 

Teraz moim problemem jest to, że ilekroć uruchomić kod pojawia się następujący błąd:

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. 

Plik Json w pytaniu wygląda to:

{ 
    "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" 
} 

Wydaje mi się, że ma problem z deserializacją zagnieżdżonego obiektu s, jak sobie z tym poradzę?

+0

odkryłem, że mam dokładnie ten sam błąd, jeśli użyłem JsonConvert.DeserializeObject i przeszedł ustawienia serializer z moim konwertera dodanej. Kiedy użyłem tego samego przeciążenia, jak to działało ... Nie wiem, czy to jest JsonSerializer w błędzie lub moje zrozumienie ustawień serializera. –

Odpowiedz

14

Po pierwsze, brak pozycji SourceType dla pozycji menu "Test podmenu" w twoim jsonie.

Po drugie, nie powinieneś po prostu używać ToObject ze względu na właściwość Submenus, którą należy obsługiwać rekursywnie.

Poniższy ReadJson zadziała:

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; 
} 
+0

Miałem zamiar zrobić "btw" po tym, jak mam to do pracy, oryginalny kod nie miał polimorfizmu, ale zdecydowałem się rozszerzyć go do modularyzacji funkcjonalności –

+1

Usunąłem to "btw".Właśnie znalazłem twoje właściwości obecnie w bazie 'MenuItem' powinno być niezbędne. : P –

0

Powodem są coraz to błąd, ponieważ klasa MenuItem jest oznaczone jako abstract. Zgaduję, że zrobiłeś to, aby wymusić implementację metody DisplayContents() w odziedziczonych klasach.

Odmienny sposób umożliwiający JSON należy czytać, co Mouhong Lin zasugerował, jest stworzenie bazy Interface dla swojej strukturze MENUITEM mają klasa MenuItem implementować interfejs z wersji podstawowej metody DisplayContents(), oznaczyć ją jako wirtualny, a następnie nadpisuje go w odziedziczonych podklasach.
To podejście zapewni, że zawsze otrzymasz coś, co wyświetlisz, dzwoniąc pod numer DisplayContents() i usuniesz błąd, który otrzymujesz.

Bardzo surowy i uproszczoną wersją klas i interfejsu:

public interface IMenuItem 
{ 
    String Title { get; set; } 
    String Contents { get; set; } 
    List<MenuItem> Submenus { get; set; } 
    String Source { get; set; } 
    String SourceType { get; set; } 
    void DisplayContents(); 
} 

public class MenuItem: IMenuItem 
{ 
    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 virtual void DisplayContents() { MessageBox.Show(Title); } 
} 

// Very very basic implementation of the classes, just to show what can be done 
public class FileMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Title + this.GetType().ToString()); } } 
public class FolderMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Title + "Folder Class"); } } 
public class JsonMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Contents); } } 
public class RestMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Source); } } 
public class RssMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(SourceType); } } 
public class TextMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Title); } } 
public class UrlMenu : MenuItem { public override void DisplayContents() { MessageBox.Show(Title); } } 
Powiązane problemy