I have approximately the following picture:
public class Foo
{
public Foo(Bar bar, String x, String y)
{
this.Bar = bar;
this.X = x;
this.Y = y;
}
[JsonIgnore]
public Bar Bar { get; private set; }
public String X { get; private set; }
public String Y { get; private set; }
}
public class Bar
{
public Bar(String z)
{
this.Z = z;
}
public String Z { get; private set; }
}
I want somehow to pass an object of type Bar to a constructor of type Foo during deserialization, i.e:
var bar = new Bar("Hello world");
var x = JsonConvert.DeserializeObject<Foo>(fooJsonString, bar);
Here are my thoughts regarding problem solution:
The problem:
Json.Net’s custom deserialization api is not transparent, i.e. affects my class hierarchy.
Actually it’s not a problem in case when you have 10-20 classes in your project, though if you have huge project with thousands of classes, you are not particularly happy about the fact that you need comply your OOP design with Json.Net requirements.
Json.Net is good with POCO objects which are populated (initialized) after they are created. But it’s not truth in all cases, sometimes you get your objects initialized inside constructor. And to make that initialization happen you need to pass ‘correct’ arguments. These ‘correct’ arguments can either be inside serialized text or they can be already created and initialized some time before. Unfortunately Json.Net during deserialization passes default values to arguments that he doesn’t understand, and in my case it always causes ArgumentNullException.
The solution:
Here is approach that allows real custom object creation during deserialization using any set of arguments either serialized or non-serialized, the main problem is that the approach sub-optimal, it requires 2 phases of deserialization per object that requires custom deserialization, but it works and allows deserializing objects the way you need it, so here goes:
First we reassemble the CustomCreationConverter class the following way:
Next we create the factory class that will create our Foo:
Here is sample code:
In this case foo contains an argument that we needed to pass to a Foo constructor during deserialization.