Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deserialize JSON to objects of the correct type, without having to define the type before hand?

I searched through similar questions and couldn't find anything that quite matched what i was looking for.

New to C# so bear with me please.

I have some json files that i am deserializing. I want the files to deserialize to objects of the correct type, without having to define the type before hand. Here's my code:

public class loadJson
    {
        //path of the file location
        public void readJson(string path)
        {
            //array of files at the path location. right now just reading one file
            FileInfo[] files = new DirectoryInfo(path).GetFiles("seleniumExample.json").ToArray();

            foreach (FileInfo fi in files)
            {

                dynamic b1 = null;
                using (StreamReader file = new StreamReader(fi.FullName))
                {

                    string fileText = file.ReadToEnd();
                    //Console.WriteLine(fileText);

                    try
                    {
                        b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText);
                    }
                    catch(Exception e)
                    {
                        Console.WriteLine("ERROR!!!! " + e.ToString());
                    }

                    file.Close();
                }
            }
        }
    }

I have a bunch of object types that I will be feeding into my program through json files.

I don't want to have to explicitly call b1 a Bid, or a Client, or any other specific predefined class. If I do explicitly call b1 a Bid, it loads all the info just fine and fills out the correct instance variables.

But when I use "dynamic", or general "object", it can't figure it out and just initializes to an "object".

Is there a way to perform generic deserialization and have it create an object of the correct class based on the fields defined in the json file?

Thanks in advance for the help, and i apologize if my question is incredibly unclear. If so, please just let me know how I can help clear up any ambiguity. Thanks again.

like image 634
kdeez Avatar asked Apr 17 '15 01:04

kdeez


1 Answers

Json.NET has the ability to record the .Net object type of polymorphic types during serialization, by using the setting TypeNameHandling = TypeNameHandling.Auto. When the setting is enabled the .Net type of polymorphic objects will appear as a synthetic property called "$type", for instance:

"$type": "Newtonsoft.Json.Samples.Stockholder, Newtonsoft.Json.Tests"

However, the "$type" property is never emitted for the root object if you call the conventional methods JsonConvert.SerializeObject(Object) or JsonSerializer.Serialize(TextWriter, Object). Instead, you must use one of the serialization methods that accepts an "expected" root type, for instance SerializeObject(Object, Type, JsonSerializerSettings) or JsonSerializer.Serialize(TextWriter, Object, Type). Passing typeof(object) as the expected type guarantees the type property will appear:

var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto };
var json = JsonConvert.SerializeObject(rootObject, typeof(object), settings);

If you create your JSON files using this setting, the JSON itself will remember the type of object serialized. This type will be used by Json.NET during deserialization, as long as you use set TypeNameHandling to something other than TypeNameHandling.None. e.g.:

var settings = new Newtonsoft.Json.JsonSerializerSettings { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto };
b1 = Newtonsoft.Json.JsonConvert.DeserializeObject(fileText, settings);

Working sample .Net fiddle here.

Caveats: this way of storing .Net types in JSON is nonstandard. Other serializers such as DataContractJsonSerializer do not process type information in this format.

Note also this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json, How to configure Json.NET to create a vulnerable web API, and Alvaro Muñoz & Oleksandr Mirosh's blackhat paper https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf.

like image 137
dbc Avatar answered Nov 03 '22 15:11

dbc