Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Json.NET perform dependency injection during deserialization?

When I have a class with no default constructor, i.e. using dependency injection to pass its dependencies, can Newtonsoft.Json create such an object?

For example:

public class SomeFoo
{
    private readonly IFooDependency _dependency;

    public SomeFoo(IFooDependency dependency){
        if(dependency == null)
            throw new ArgumentNullException("dependency");

        _dependency = dependency;
    }

    public string Data { get; set; }
    public int MoreData { get; set; }

    public void DoFoo(){
        Data = _dependency.GetFooData();
        MoreData = _dependency.GetMoreFooDate();
    }
}

During serialization, I only care of storing Data and MoreData (and the type of the object, but let's don't complicate things for the moment). Now, to deserialize, can I call something like

var obj = JsonConvert.DeserializeObject<SomeFoo>(jsonText);

How can I let JsonConvert know about my DI container?

(Note: A work-around would be to always have default constructors in my classes, and call Service Locator in there to get any dependencies I need. I'm just looking for some more clean solution without poluting my classes with such constructors).

like image 832
zafeiris.m Avatar asked Feb 18 '14 16:02

zafeiris.m


People also ask

How does JSON deserialization work?

In Deserialization, it does the opposite of Serialization which means it converts JSON string to custom . Net object. In the following code, it calls the static method DeserializeObject() of the JsonConvert class by passing JSON data. It returns a custom object (BlogSites) from JSON data.

What is JSON deserialization?

JSON is a format that encodes objects in a string. Serialization means to convert an object into that string, and deserialization is its inverse operation (convert string -> object).

How do I use JSON deserializer?

A common way to deserialize JSON is to first create a class with properties and fields that represent one or more of the JSON properties. Then, to deserialize from a string or a file, call the JsonSerializer. Deserialize method.

Can JsonConvert DeserializeObject throw?

DeserializeObject can throw several unexpected exceptions (JsonReaderException is the one that is usually expected). These are: ArgumentException.


2 Answers

You shouldn't let JsonConvert know anything about your DI container. The problems you're experiencing are caused by a flaw in the design of your application. The flaw here is that you mix data and behavior.

If you separate the data from the behavior your problem (and many other problems) will simply go away. You can do this by creating two classes: one for the data, and one for the behavior:

public class SomeFoo {     public string Data { get; set; }     public int MoreData { get; set; } }  public class SomeFooHandler {     private readonly IFooDependency _dependency;      public SomeFooHandler(IFooDependency dependency) {         _dependency = dependency;     }      public void Handle(SomeFoo foo) {         foo.Data = _dependency.GetFooData();         foo.MoreData = _dependency.GetMoreFooDate();     } } 

Since now data and behavior are separated, SomeFoo can be serialized without any problem and SomeFooHandler can simply be injected. SomeFoo has becomes a Parameter Object.

like image 66
Steven Avatar answered Sep 23 '22 15:09

Steven


I agree with the separation of concerns posted by Steven, and the answer Mark Seemann has posted here. However, if you still want to go this way, here is a solution that may help:

Inherit a CustomCreationConverter<T>:

internal class NinjectCustomConverter<T> : CustomCreationConverter<T> where T : class
{
    private readonly IResolutionRoot _serviceLocator;

    public NinjectCustomConverter(IResolutionRoot serviceLocator)
    {
        _serviceLocator = serviceLocator;
    }

    public override T Create(Type objectType)
    {
        return _serviceLocator.Get(objectType) as T;
    }
}

Then make sure you retrieve this converter instance via your DI container as well. The code below will deserialize and perform DI on your object:

var ninjectConverter = kernel.Get<NinjectCustomConverter<SerializedObject>>();
var settings = new JsonSerializerSettings();
settings.Converters.Add(ninjectConverter);

var instance = JsonConvert.DeserializeObject<SerializedObject>(json, settings);

Here is a complete working example.

like image 43
cvbarros Avatar answered Sep 19 '22 15:09

cvbarros