8

Not quite the same as this:

How do I Inject Dependencies with Ninject, where instances are deserialised from json

Where the answer is that your data class that you deserialized shouldn't need a service anyway. Is there a way to use dependency inject with a class derived from JsonConverter? For example, if you had this:

[JsonConverter(typeof(MyCustomConverter))]
public class Foo
{
    public string SomeProp { get; set; }
}

And:

public class MyCustomConverter : JsonConverter
{
    private readonly IMyService myService;

    public MyCustomConverter(IMyService _myService)
    {
        myService = _myService;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var bar = myService.SomeFunctionThatMightEffectDeserialization();
        //...
    }
}

Is there anyway to hook into how JSON.Net instantiates MyCustomConverter to get it to let Ninject do it's thing?

EDIT This is NOT injecting a service into Foo like the suggested dupe. This is injecting only into MyCustomConverter so that it can then deserialize Foo.

Community
  • 1
  • 1
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • 4
    @StriplingWarrior: The duplicate isn't quite the same. I'm not looking to inject *into* the object I'm deserializing. The object I'm deserializing doesn't need any services, only the json converter needs dependency injection. – Matt Burland Aug 10 '15 at 19:46
  • 1
    Could you add some static (thread static?) event to your `MyCustomConverter` type that allows an `IMyService` to be returned in custom event args. Awkward, but [doable](http://stackoverflow.com/questions/1210026/return-a-value-from-an-event-is-there-a-good-practice-for-this). Also, maybe see here: http://blog.ploeh.dk/2013/09/08/di-and-events-third-party-connect/ – dbc Aug 10 '15 at 20:00
  • @dbc: An interesting idea, I'd have to think about that one. – Matt Burland Aug 10 '15 at 20:05
  • @MattBurland: Nevermind, you're right. Carry on. – StriplingWarrior Aug 10 '15 at 20:06
  • what creates the converter? that would be responsible for injecting the dependencies – Dave Thieben Aug 10 '15 at 20:06
  • 1
    @davethieben quite obviously newtonsoft.json – Andrew Savinykh Aug 10 '15 at 20:14
  • @davethieben: Well that's the question. The `JsonConverterAttribute` is sealed, so I can't derive. Somewhere in the internal workings of JSON.Net that attribute gets read when it tries to deserialize that class and an instance of `MyCustomConverter` gets created. The key part of the question is whether or not there's a place to hook into that process. – Matt Burland Aug 10 '15 at 20:23
  • @MattBurland I don't think you can do it without accessing the kernel, which really should not be accessed outside of composition root. json.net does not support participating in a DI pipeline, so all you left with is workarounds. – Andrew Savinykh Aug 10 '15 at 20:24
  • 1
    @MattBurland [`JsonTypeReflector`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs) is the class responsible for the instantication, if you'd like to have a look. – Andrew Savinykh Aug 10 '15 at 20:25
  • @MattBurland - Have a look at [this answer](http://stackoverflow.com/questions/27245220/how-can-i-test-for-the-presence-of-an-action-filter-with-constructor-arguments/27470397#27470397). If you define attributes without behavior, you can use a class with behavior (that is injected with DI) to read their metadata. – NightOwl888 Aug 10 '15 at 21:28
  • @NightOwl888: I've seen (and used) similar stuff with filters using `BindHttpFilter` in ninject, but I'm not sure if it's generally possible. I need the attribute `JsonConverter(typeof(MyCustomConverter))` for JSON.Net, but I need json.net to let DI construct the instance of `MyCustomConverter` and that seems to be buried in `JsonTypeReflector` which is `internal static` – Matt Burland Aug 17 '15 at 21:07

1 Answers1

1

It's a bit of a hack, but it's possible to do something similar by setting the ContractResolver in the JsonSerializerSettings. In this case, using Autofac:

var builder = new ContainerBuilder();
builder.RegisterInstance(myService);
var container = builder.Build();

var settings = new JsonSerializerSettings
{
    ContractResolver = new AutofacContractResolver(container),
};

and then in the converter:

var jsonContract = serializer.ContractResolver.ResolveContract(typeof(IMyService));
var service = (IMyService)jsonContract.DefaultCreator();

So you're not really injecting the service into the converter, but at least you can access it without a concrete dependency. Also you're not using the same Autofac container as your app but creating a new one. Not ideal, but it's something :)

gezzahead
  • 1,196
  • 1
  • 11
  • 20