Choose best Class for Deserialization...

Aug 24, 2009 at 6:15 PM
Edited Aug 24, 2009 at 7:52 PM

If I am passed json data from an external source, and know it is going to be in one of a few certain formats, but don't know which one, what would be the best way to "automatically" choose the proper class to deserialize to?  The following code could be used as a basis... not sure what the most elegant way of doing this is, or if there is even a proper way built into Json.Net... nothing that I have found at least...

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace JsonTest
{
	class Program
	{
		static void Main(string[] args)
		{
			string JsonSource = @"{""intTest"":1,""stringTest"":""Test String"",""boolTest"":false}";
			//string JsonSource = @"{""intTest1"":7,""intTest2"":8,""intTest3"":9}";
			//string JsonSource = @"{""intTest"":99,""SubObject"":{""boolSubTest"":true,""stringSubTest"":""Test String""}}";

			/// Code here to automatically pick the class to deserialize the JsonSource into...

			Console.ReadKey();
		}
	}

	public class JsonA
	{
		public int intTest { get; set; }
		public string stringTest { get; set; }
		public bool boolTest { get; set; }
	}

	public class JsonB
	{
		public int intTest1 { get; set; }
		public int intTest2 { get; set; }
		public int intTest3 { get; set; }
	}

	public class JsonC
	{
		public int intTest { get; set; }
		public SubObject objTest { get; set; }

		public class SubObject
		{
			bool boolSubTest { get; set; }
			string stringSubTest { get; set; }
		}
	}
}



 

Aug 24, 2009 at 8:57 PM
Edited Aug 24, 2009 at 9:11 PM

I have figured out a way to do this that suits my needs, however there may be a more elegant solution, plus my solution requires a change to the JsonSchema class as well as the JsonSchemaGenerator class...

 

Code for sample app...

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace JsonTest
{
	class Program
	{
		static void Main(string[] args)
		{
			string JsonSourceA = @"{""intTest"":1,""stringTest"":""Test String"",""boolTest"":false}";
			string JsonSourceB = @"{""intTest1"":7,""intTest2"":8,""intTest3"":9}";
			string JsonSourceC = @"{""intTest"": 3,""objTest"": {""boolSubTest"": false,""stringSubTest"": ""TestString""}}";
			string JsonSourceD = @"{""SubObject"":{""boolSubTest"":true,""stringSubTest"":""Test String""}}";

			Newtonsoft.Json.Schema.JsonSchemaGenerator schemaGenerator = new Newtonsoft.Json.Schema.JsonSchemaGenerator();
			
			Newtonsoft.Json.Schema.JsonSchemaResolver r = new Newtonsoft.Json.Schema.JsonSchemaResolver();
			r.LoadedSchemas.Add(schemaGenerator.Generate(typeof(JsonA)));
			r.LoadedSchemas.Add(schemaGenerator.Generate(typeof(JsonB)));
			r.LoadedSchemas.Add(schemaGenerator.Generate(typeof(JsonC)));

			Newtonsoft.Json.Linq.JToken ja = Newtonsoft.Json.Linq.JObject.Parse(JsonSourceA);
			Console.WriteLine(FindSchemaType(ja, r).ToString());

			Newtonsoft.Json.Linq.JToken jb = Newtonsoft.Json.Linq.JObject.Parse(JsonSourceB);
			Console.WriteLine(FindSchemaType(jb, r).ToString());

			Newtonsoft.Json.Linq.JToken jc = Newtonsoft.Json.Linq.JObject.Parse(JsonSourceC);
			Console.WriteLine(FindSchemaType(jc, r).ToString());

			Newtonsoft.Json.Linq.JToken jd = Newtonsoft.Json.Linq.JObject.Parse(JsonSourceD);
			Console.WriteLine(FindSchemaType(jd, r).ToString());

			Console.ReadKey();
		}

		public static Type FindSchemaType(Newtonsoft.Json.Linq.JToken jsonSource, Newtonsoft.Json.Schema.JsonSchemaResolver jsonResolver)
		{
			foreach (Newtonsoft.Json.Schema.JsonSchema schema in jsonResolver.LoadedSchemas)
			{
				if (Newtonsoft.Json.Schema.Extensions.IsValid(jsonSource, schema))
				{
					return schema.BaseType;
				}

			}

			return jsonSource.GetType();
		}
	}

	public class JsonA
	{
		public int intTest { get; set; }
		public string stringTest { get; set; }
		public bool boolTest { get; set; }
	}

	public class JsonB
	{
		public int intTest1 { get; set; }
		public int intTest2 { get; set; }
		public int intTest3 { get; set; }
	}

	public class JsonC
	{
		public int intTest { get; set; }
		public SubObject objTest { get; set; }

		public class SubObject
		{
			public bool boolSubTest { get; set; }
			public string stringSubTest { get; set; }
		}
	}
}

 

 

Changes needed in JsonSchema - New property...

 

public Type BaseType { get; set; }

 

 

Changes needed in JsonSchemaGenerator - Set value of new BaseType property in GenerateInternal method right before popping the current schema...

 

CurrentSchema.BaseType = type;

 

 

 

Question is... is there a better way to do this that I am missing?  If not, any chance of getting the BaseType or something similar added to the official code?

 

Edit: Looks like what I really want to do here though is going to have to wait for C# 4.0 dynamic types :-(