0

An API server endpoint can return one of the followings:

{
    "type": "apple",
    "color": "red"
}

which can be deserialized into the class Apple

or

{
    "type": "orange",
    "size": "big"
}

which can be deserialized into the class Orange.

I've created a class Fruit with just one property fruitInstance (which is an object) and it will store an instance of either Apple or Orange.

Is there a way I can customize the JSON.NET deserializer of Fruit to do the following?

  • Go through possible fruit types one by one (only Apple, Orange in this case but we can support more later)
  • Try to deserialize the response (JSON string) into Apple for example
  • If the deserialization throws exception, try next one (e.g. Orange) until the deserialization is successful
  • On success, store the deserialized instance (e.g. Orange) in the frusitInstance property

UPDATE: Fruit is not a parent class of Apple/Orange and we cannot make it so due to requirement. In other words, we cannot use JsonSubTypes. Let's imagine later the payload can be an integer (e.g. 15) and Fruit will be able to deserialize the payload (integer) into an Integer and store it in fruitInstance.

Amanda
  • 9
  • 2
  • 1
    You can create a custom [`JsonConverter`](https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm) to deserialize your class hierarchy. Please see [Json.Net Serialization of Type with Polymorphic Child Object](https://stackoverflow.com/q/29528648/3744182), [Deserializing polymorphic json classes without type information using json.net](https://stackoverflow.com/q/19307752/3744182) or [How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?](https://stackoverflow.com/q/8030538/3744182) for details. – dbc Sep 29 '20 at 16:41
  • Do those three questions answer yours also, or do you need more help? If so, can you [edit] your question to share a [mcve], especially the `Fruit` class as well as `Apple` and `Orange`? Also, why are you using composition instead of inheritance for `Fruit`? – dbc Sep 29 '20 at 17:32
  • `JsonSubtypes` is possible to use here. – jaabh Sep 29 '20 at 18:19
  • Thanks for pointing out JsonSubTypes but unfortunately Fruit is not a parent class of Apple/Orange and we cannot make it so due to requirement – Amanda Sep 30 '20 at 03:46

1 Answers1

0

There might be a good reason you want to write a custom JsonConverter but the problem you described here can be tackled through another approach too if you have some control over JSON being received. You can consider Fruit as your base and Apple, Orange (and so on) as its derived types. Then you can Deserialize with a specific overload of function JsonConvert.DeserializeObject<Fruit> where you can specify which derived type it should deserialize to.

Try the below code:

namespace Console_SO
{
    public abstract class Fruit
    {
        public string Color { get; set; }
    }
    public class Apple : Fruit
    {
        public string Type { get; set; }
    }
    class Orange : Fruit {
        public string Type { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //Apple
            var json = "{'$type': 'Console_SO.Apple, Console_SO','type':'Apple','color':'red'}";
            var fruit = JsonConvert.DeserializeObject<Fruit>(json, new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto
            });
            //Orange
             json = "{'$type': 'Console_SO.Orange, Console_SO','type':'Orange','color':'red'}";
             fruit = JsonConvert.DeserializeObject<Fruit>(json, new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto
            });
        }
    }

}

When sending the JSON to API you need to include a special $type property which holds the resolution details for the desired target derived type. TypeNameHandling. Lastly, you should know that this approach is not perfectly safe and opens up code to some vulnerabilities. It may or may not be a concern depending on your environment. More details on this and workarounds here.

gkulshrestha
  • 855
  • 1
  • 6
  • 11
  • Thanks for suggesting `DeserializeObject`but unfortunately Fruit is not a parent class of Apple/Orange and we cannot make it so due to requirement. See the updated question for more information. – Amanda Sep 30 '20 at 15:41