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

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

Using JSON for polymorphic types in C#, NET type name when serializing into a JSON object structure. NET type name when the type of the object being serialized is not the same as its declared type. The deserialization works and the classes pass smoothly from client if the same piece of code is serializing and deserializing (e.g. to a data  Deserializing polymorphic types without using assembly name, with just Newtonsoft lib 0 Deserialize JSON object to specific instance created on the fly in run time by string name of the class

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

Deserializing JSON into polymorphic classes with System.Text.Json , NET type to generate a class like ATimeZone above. Seems feasible! Note the className property in the above JSON, it gives me information  possible duplicate of Deserializing polymorphic json classes without type information using json.net – Brian Rogers Feb 18 '15 at 14:22.

Advanced to Brian Rogers answer. And about "use Serializer.Populate() instead of item.ToObject()". If derived types has contstructors or some of their has own customconverter you must use general way for deserialize JSON. So you must leave work for instantiate new object to NewtonJson. This way you can achieve it in you CustomJsonConverter:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    ..... YOU Code For Determine Real Type of Json Record .......

    // 1. Correct ContractResolver for you derived type
    var contract = serializer.ContractResolver.ResolveContract(DeterminedType);
    if (converter != null && !typeDeserializer.Type.IsAbstract && converter.GetType() == GetType())
    {
        contract.Converter = null; // Clean Wrong Converter grabbed by DefaultContractResolver from you base class for derived class
    }

    // Deserialize in general way           
    var jTokenReader = new JTokenReader(jObject);
    var result = serializer.Deserialize(jTokenReader, DeterminedType);

    return (result);
}

This work if you have recursion of objects.

TypeNameHandling setting, TypeNameHandling setting to include type information when serializing are created when deserializing JSON. Sample. Types. Copy. public abstract class  Serialize properties of derived classes. Serialization of a polymorphic type hierarchy is not supported. For example, if a property is defined as an interface or an abstract class, only the properties defined on the interface or abstract class are serialized, even if the runtime type has additional properties.

Following implementation should let you de-serialize without changing the way you have designed your classes and by using a field other than $type to decide what to de-serialize it into.

public class GalleryImageConverter : JsonConverter
{   
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(GalleryImage) || objectType == typeof(GalleryAlbum));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            if (!CanConvert(objectType))
                throw new InvalidDataException("Invalid type of object");
            JObject jo = JObject.Load(reader);
            // following is to avoid use of magic strings
            var isAlbumPropertyName = ((MemberExpression)((Expression<Func<GalleryImage, bool>>)(s => s.is_album)).Body).Member.Name;
            JToken jt;
            if (!jo.TryGetValue(isAlbumPropertyName, StringComparison.InvariantCultureIgnoreCase, out jt))
            {
                return jo.ToObject<GalleryImage>();
            }
            var propValue = jt.Value<bool>();
            if(propValue) {
                resultType = typeof(GalleryAlbum);
            }
            else{
                resultType = typeof(GalleryImage);
            }
            var resultObject = Convert.ChangeType(Activator.CreateInstance(resultType), resultType);
            var objectProperties=resultType.GetProperties();
            foreach (var objectProperty in objectProperties)
            {
                var propType = objectProperty.PropertyType;
                var propName = objectProperty.Name;
                var token = jo.GetValue(propName, StringComparison.InvariantCultureIgnoreCase);
                if (token != null)
                {
                    objectProperty.SetValue(resultObject,token.ToObject(propType)?? objectProperty.GetValue(resultObject));
                }
            }
            return resultObject;
        }
        catch (Exception ex)
        {
            throw;
        }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Deserializing Derived Types with JSON.NET, NET to serialize objects which involve inheritance hierarchies, from Person), and then deserializing into a Person (the base class). Due to polymorphism, we'd expect the result to hold a reference to an Alas, this is not the case: actual serialized JSON has changed: there is now type information in it,  This tutorial details how to deserialize* a JSON “string” into a c# object using JSON.net by Newtonsoft. It’s a stand alone tutorial, but follows logically from Consuming a REST API from c# . * It pains me to use a ‘z’ here but I need to keep the US audiences and search engines happy!

I'm only posting this to clear up some of the confusion. If you are working with a predefined format and need to deserialize it, this is what I found worked best and demonstrates the mechanics so that others can tweak it as needed.

public class BaseClassConverter : JsonConverter
    {
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var j = JObject.Load(reader);
            var retval = BaseClass.From(j, serializer);
            return retval;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }

        public override bool CanConvert(Type objectType)
        {
            // important - do not cause subclasses to go through this converter
            return objectType == typeof(BaseClass);
        }
    }

    // important to not use attribute otherwise you'll infinite loop
    public abstract class BaseClass
    {
        internal static Type[] Types = new Type[] {
            typeof(Subclass1),
            typeof(Subclass2),
            typeof(Subclass3)
        };

        internal static Dictionary<string, Type> TypesByName = Types.ToDictionary(t => t.Name.Split('.').Last());

        // type property based off of class name
        [JsonProperty(PropertyName = "type", Required = Required.Always)]
        public string JsonObjectType { get { return this.GetType().Name.Split('.').Last(); } set { } }

        // convenience method to deserialize a JObject
        public static new BaseClass From(JObject obj, JsonSerializer serializer)
        {
            // this is our object type property
            var str = (string)obj["type"];

            // we map using a dictionary, but you can do whatever you want
            var type = TypesByName[str];

            // important to pass serializer (and its settings) along
            return obj.ToObject(type, serializer) as BaseClass;
        }


        // convenience method for deserialization
        public static BaseClass Deserialize(JsonReader reader)
        {
            JsonSerializer ser = new JsonSerializer();
            // important to add converter here
            ser.Converters.Add(new BaseClassConverter());

            return ser.Deserialize<BaseClass>(reader);
        }
    }

JsonSerializer polymorphic deserialization support · Issue #30083 , public class FooBase public class FooA : FooBase public class FooB : FooBase Serialized with Newtonsoft.JSON JsonSerializerSettings Settings They were primarily intended for data-type converters, not for objects and collections. JsonSerializer polymorphic deserialization support on Nov 22, 2019. Deserializing Derived Types with JSON.NET January 24, 2015 Gigi 6 Comments If you’re using Json.NET to serialize objects which involve inheritance hierarchies, there’s a little issue you might run into.

How to serialize and deserialize JSON using C#, Text.Json namespace to serialize to and deserialize from JSON in .NET. for access to data; Use JsonDocument to write JSON; Use Utf8JsonWriter; Use Utf8JsonReader; Additional resources The code examples refer to the following class and variants of it: Serialization of a polymorphic type hierarchy is not supported. Json.NET cannot serialize a NameValueCollection without a custom converter because NameValueCollection implements IEnumerable for iterating over the keys, but does not implement IDictionary for iterating over keys and values. See this answer for a fuller explanation of why this causes problems for Json.NET.

How to write custom converters for JSON serialization, Json with functionality not included in the current release. Support polymorphic deserialization. Support The following sample is a converter that overrides default serialization for an existing data type. Json.Serialization; namespace SystemTextJsonSamples { public class DateTimeOffsetConverter  This sample uses the T:Newtonsoft.Json.TypeNameHandling setting to include type information when serializing JSON and read type information so that the create types are created when deserializing JSON.

Newtonsoft json ignore null, Json 12. NET Serialization; c# - Serializing null in JSON. Mar 14, 2014 · Writing Deserializing polymorphic json classes without type information using json. La désérialisation polymorphes json classes sans informations de type à l'aide de json.net Ce Imgur api appel renvoie une liste contenant à la fois Image de la Galerie et Galerie de l'Album classes représentées en json.

Comments
  • You want to take the Json string and put it into classes? And I'm confused by what you mean by there is no $type property.
  • Yes, I have the json string and want to deserialize to C# classes. Json.NET appears to use a property called $type to draw a distinction between different types held in an array. This data doesnt have that property and just uses the 'IsAlbum' property.
  • This does not work if polymorphic objects are recursive, i.e. if an Album may contain other albums. In the converter one should use Serializer.Populate() instead of item.ToObject(). See stackoverflow.com/questions/29124126/…
  • For anyone trying out this approach and finding it results in an infinite loop (and eventually a stack overflow) you might want to use the Populate approach instead of ToObject. See the answers to stackoverflow.com/questions/25404202/… and stackoverflow.com/questions/29124126/… . I have an example of the two approaches in a Gist here: gist.github.com/chrisoldwood/b604d69543a5fe5896a94409058c7a95 .
  • I was lost in various answers about 8 hours, they were about CustomCreationConverter. Finally this answer worked and I'm feeling enlightened. My inner object contains it's type as string and I use it to conversion like this. JObject item = JObject.Load(reader); Type type = Type.GetType(item["Type"].Value<string>()); return item.ToObject(type);
  • This is working fine for me provided that you do not put the converter attribute on the base class. The converter must be injected into the serializer (via settings etc) and check for only the base type in CanConvert. I am debating using Populate() but I really don't like either method.
  • I have fixed the converter to use JsonSerializer.Populate() instead of JObject.ToObject() as was suggested by Ivan and Chris. This will avoid problems with recursive loops and allow the converter to be used successfully with attributes.
  • This should be the top answer. I've spent the better part of a day working on a solution to this problem examining custom JsonConverter classes from dozens of authors. Your nuget package replaced all that effort with three lines of code. Well done sir. Well done.
  • In Java world, Jackson library provides similar support via @JsonSubTypes attribute. See stackoverflow.com/a/45447923/863980 for another use-case example (also Cage/Animal example by @KonstantinPelepelin in comments).
  • This is the simplest answer indeed, but unfortunately it comes at a performance cost. I found out that deserialization is 2-3 times faster with a hand-written converter (as shown in the answer of @BrianRogers)
  • Hi @SvenVranckx, feel free to open an issue on github.com/manuc66/JsonSubTypes
  • This code is not safe in mutithreaded contexts, because the default contract resolver caches contracts. Setting options on the contract (like its converter) will cause it to behave differently on concurrent, and even subsequent, calls.
  • serializer.ContractResolver.ResolveContract(DeterminedType) - return already cached contract. So contract.Converter = null; change cached object. It only change reference on cached object, and it is thread safe.
  • How do you use this when using implicit conversion without using the [JsonConverter()] attribute (which is commented as "important")? Eg: deserializing via [FromBody] attribute?
  • I assume you can simply edit the global JsonFormatter's settings to include this converter. See stackoverflow.com/questions/41629523/…