Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserializing polymorphic json classes without type information using json.net

This Imgur api call returns a list containing both Gallery Image and Gallery Album classes represented in JSON.

I can't see how to deserialize these automatically using Json.NET given that there is no $type property telling the deserializer which class is meant to be represented. There is a property called "IsAlbum" that can be used to differentiate between the two.

This question appears to show one method but it looks like a bit of a hack.

How do I go about deserializing these classes? (using C#, Json.NET).

Sample Data:

Gallery Image

{     "id": "OUHDm",     "title": "My most recent drawing. Spent over 100 hours.",         ...     "is_album": false } 

Gallery Album

{     "id": "lDRB2",     "title": "Imgur Office",     ...     "is_album": true,     "images_count": 3,     "images": [         {             "id": "24nLu",             ...             "link": "http://i.imgur.com/24nLu.jpg"         },         {             "id": "Ziz25",             ...             "link": "http://i.imgur.com/Ziz25.jpg"         },         {             "id": "9tzW6",             ...             "link": "http://i.imgur.com/9tzW6.jpg"         }     ] } } 
like image 210
Peter Kneale Avatar asked Oct 10 '13 23:10

Peter Kneale


People also ask

Is polymorphic deserialization possible in System text JSON?

There is no polymorphic deserialization (equivalent to Newtonsoft. Json's TypeNameHandling ) support built-in to System.

What is polymorphic JSON?

You can use this schema when defining XML Type hierarchies by using only the base XML Types. The XML schema defines XML Types that inherit from each other. In the JSON, an object carries no additional information about the type.

What is Deserializing a JSON?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).

Which one is correct class for JsonSerializer?

The JsonSerializer is a static class in the System. Text. Json namespace. It provides functionality for serializing objects to a JSON string and deserializing from a JSON string to objects.


2 Answers

You can do this fairly easily by creating a custom JsonConverter to handle the object instantiation. Assuming you have your classes defined something like this:

public abstract class GalleryItem {     public string id { get; set; }     public string title { get; set; }     public string link { get; set; }     public bool is_album { get; set; } }  public class GalleryImage : GalleryItem {     // ... }  public class GalleryAlbum : GalleryItem {     public int images_count { get; set; }     public List<GalleryImage> images { get; set; } } 

You would create the converter like this:

public class GalleryItemConverter : JsonConverter {     public override bool CanConvert(Type objectType)     {         return typeof(GalleryItem).IsAssignableFrom(objectType);     }      public override object ReadJson(JsonReader reader,          Type objectType, object existingValue, JsonSerializer serializer)     {         JObject jo = JObject.Load(reader);          // Using a nullable bool here in case "is_album" is not present on an item         bool? isAlbum = (bool?)jo["is_album"];          GalleryItem item;         if (isAlbum.GetValueOrDefault())         {             item = new GalleryAlbum();         }         else         {             item = new GalleryImage();         }          serializer.Populate(jo.CreateReader(), item);          return item;     }      public override bool CanWrite     {         get { return false; }     }      public override void WriteJson(JsonWriter writer,          object value, JsonSerializer serializer)     {         throw new NotImplementedException();     } } 

Here's an example program showing the converter in action:

class Program {     static void Main(string[] args)     {         string json = @"         [             {                 ""id"": ""OUHDm"",                 ""title"": ""My most recent drawing. Spent over 100 hours."",                 ""link"": ""http://i.imgur.com/OUHDm.jpg"",                 ""is_album"": false             },             {                 ""id"": ""lDRB2"",                 ""title"": ""Imgur Office"",                 ""link"": ""http://alanbox.imgur.com/a/lDRB2"",                 ""is_album"": true,                 ""images_count"": 3,                 ""images"": [                     {                         ""id"": ""24nLu"",                         ""link"": ""http://i.imgur.com/24nLu.jpg""                     },                     {                         ""id"": ""Ziz25"",                         ""link"": ""http://i.imgur.com/Ziz25.jpg""                     },                     {                         ""id"": ""9tzW6"",                         ""link"": ""http://i.imgur.com/9tzW6.jpg""                     }                 ]             }         ]";          List<GalleryItem> items =              JsonConvert.DeserializeObject<List<GalleryItem>>(json,                  new GalleryItemConverter());          foreach (GalleryItem item in items)         {             Console.WriteLine("id: " + item.id);             Console.WriteLine("title: " + item.title);             Console.WriteLine("link: " + item.link);             if (item.is_album)             {                 GalleryAlbum album = (GalleryAlbum)item;                 Console.WriteLine("album images (" + album.images_count + "):");                 foreach (GalleryImage image in album.images)                 {                     Console.WriteLine("    id: " + image.id);                     Console.WriteLine("    link: " + image.link);                 }             }             Console.WriteLine();         }     } } 

And here is the output of the above program:

id: OUHDm title: My most recent drawing. Spent over 100 hours. link: http://i.imgur.com/OUHDm.jpg  id: lDRB2 title: Imgur Office link: http://alanbox.imgur.com/a/lDRB2 album images (3):     id: 24nLu     link: http://i.imgur.com/24nLu.jpg     id: Ziz25     link: http://i.imgur.com/Ziz25.jpg     id: 9tzW6     link: http://i.imgur.com/9tzW6.jpg 

Fiddle: https://dotnetfiddle.net/1kplME

like image 131
Brian Rogers Avatar answered Sep 21 '22 14:09

Brian Rogers


Simply with JsonSubTypes attributes that work with Json.NET

    [JsonConverter(typeof(JsonSubtypes), "is_album")]     [JsonSubtypes.KnownSubType(typeof(GalleryAlbum), true)]     [JsonSubtypes.KnownSubType(typeof(GalleryImage), false)]     public abstract class GalleryItem     {         public string id { get; set; }         public string title { get; set; }         public string link { get; set; }         public bool is_album { get; set; }     }      public class GalleryImage : GalleryItem     {         // ...     }      public class GalleryAlbum : GalleryItem     {         public int images_count { get; set; }         public List<GalleryImage> images { get; set; }     } 
like image 38
manuc66 Avatar answered Sep 21 '22 14:09

manuc66