I am using Hangfire
to run multiple tasks of my custom interface
, let's call it IType
. However, since Hangfire
serializes the method, thereby destroying the instance of that type so when it tries to call it I get an error message like this:
Newtonsoft.Json.JsonSerializationException: Could not create an instance of type IType. Type is an interface or abstract class and cannot be instantiated. Path 'Something', line 1, position 17. at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject(...
I figure that one way around this is for each class
that is an instance of my interface
can store its fully qualified domain name
, then I can use reflection
to return it the type that it needs, only problem is I don't know how to get Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject()
to call my deserialization method. Is there some attribute
or some specific method name I need to have in order to make this work?
You need to specify a JsonSerializerSettings
that includes a suitable TypeNameHandling
value. That will embed the fully qualified name in the JSON in a $type
property, which it can then use to deserialize. Here's a complete example:
using System;
using Newtonsoft.Json;
interface IFoo
{
void Method();
}
class Foo1 : IFoo
{
public string Name { get; set; }
public void Method() => Console.WriteLine("Method in Foo1");
}
class Foo2 : IFoo
{
public int Value { get; set; }
public void Method() => Console.WriteLine("Method in Foo2");
}
class Root
{
public IFoo First { get; set; }
public IFoo Second { get; set; }
}
class Test
{
static void Main()
{
Root root = new Root
{
First = new Foo1 { Name = "Fred" },
Second = new Foo2 { Value = 10 }
};
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string json = JsonConvert.SerializeObject(root, settings);
Console.WriteLine(json);
Root root2 = JsonConvert.DeserializeObject<Root>(json, settings);
root2.First.Method();
root2.Second.Method();
}
}
Output, showing both the JSON and the fact that the interface properties in Root
have been suitably deserialized:
{"$type":"Root, Test","First":{"$type":"Foo1, Test","Name":"Fred"},"Second":{"$type":"Foo2, Test","Value":10}}
Method in Foo1
Method in Foo2
There are other TypeNameHandling
values you might want to use instead of All
- see the documentation for details.