I've written a custom JsonConverter
that I can assign to JsonSerializerSettings
and use with the generic override of JsonConvert.DeserializeObject
just fine:
var settings = new JsonSerializerSettings()
{
TypeNameHandling = TypeNameHandling.All,
Converters = new List<JsonConverter>() { new MyConverter() }
};
var x = JsonConvert.DeserializeObject<MyType>(input, settings);
The serialized Json was built also using TypeNameHandling.All
so it contains type information in a $type
field.
In some scenarios, though, I do not know what type was serialized and would like to use the non-generic override of DeserializeObject
. I was expecting that, if I use the same settings and/or converter and the Json contained type information that the engine would be able to process the Json correctly.
But it appears that my custom converter is only used for nested objects within the Json and not for the top-most level - despite the $type
on each level.
My problem is that without my custom converter I need a default constructor for the class. If I implement that - just for testing - then DeserializeObject indeed returns the correct type. But that isn't a real-life option: amongst other things the custom converter resolves the required instances using an IOC container and then populates them.
Am I missing something or is what I'm asking for simply not possible?
EDIT: Because it was requested, below is some example code. In this example deserializing worked without the default constructor, apparently the other one was called with null values (or default(T)
). But the basic problem still exists: the ExampleConverter
is not used as I would expect.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
namespace UnitTests.Serialization
{
public class Example
{
public Example()
{
Console.WriteLine("...Example Default Ctor...");
}
public Example(Guid guid)
{
Console.WriteLine("...Example Ctor: " + guid.ToString());
}
public string ExampleProp { get; set; }
}
public class ExampleConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer){throw new NotImplementedException();}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer){
Console.WriteLine("...ExampleConverter.ReadJson...");
var result = new Example(Guid.Empty);
serializer.Populate(reader, result);
return result;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Example);
}
}
[TestClass]
public class JsonTests
{
[TestMethod]
public void TestDeserializeObject()
{
var writeSettings = new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.All,};
var readSettings = new JsonSerializerSettings() {TypeNameHandling = TypeNameHandling.All,Converters = new List<JsonConverter>() { new ExampleConverter() }};
Console.WriteLine("Creating Example...");
var e1 = new Example(Guid.NewGuid()) { ExampleProp = "some value"};
Console.WriteLine("\nSerializing e1...");
var json = Newtonsoft.Json.JsonConvert.SerializeObject(e1, writeSettings);
Console.WriteLine("e1: " + json);
Console.WriteLine("\nDeserializing e2 - using DeserializeObject<Example>...");
var e2 = Newtonsoft.Json.JsonConvert.DeserializeObject<Example>(json, readSettings);
Console.WriteLine("e2: " + Newtonsoft.Json.JsonConvert.SerializeObject(e2, writeSettings));
Console.WriteLine("\nDeserializing e3 - using DeserializeObject...");
var e3 = Newtonsoft.Json.JsonConvert.DeserializeObject(json, readSettings);
Console.WriteLine("e3: " + Newtonsoft.Json.JsonConvert.SerializeObject(e2, writeSettings));
}
}
}
Output:
Creating Example...
...Example Ctor: d860aa00-4493-4ab0-b681-f0af7b123212
Serializing e1...
e1: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}
Deserializing e2 - using DeserializeObject<Example>...
...ExampleConverter.ReadJson...
...Example Ctor: 00000000-0000-0000-0000-000000000000
e2: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}
Deserializing e3 - using DeserializeObject...
...Example Default Ctor...
e3: {"$type":"UnitTests.Serialization.Example, UnitTests","ExampleProp":"some value"}
EDIT: I also found this, but the answer appears to be wrong: How to deserialize JSON to objects of the correct type, without having to define the type before hand?