有什么方法可以让 Newtonsoft.Create 在界面上调用您的自定义创建方法吗?
Is there some way to have Newtonsoft.Create call your custom creation method on an interface?
我正在使用 Hangfire
到 运行 我的自定义 interface
的多个任务,我们称之为 IType
。但是,由于 Hangfire
序列化该方法,从而破坏了该类型的实例,因此当它尝试调用它时,我收到如下错误消息:
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(...
我认为解决这个问题的一种方法是让作为我的 interface
实例的每个 class
可以存储它的 fully qualified domain name
,然后我可以使用 reflection
来return 它是它需要的类型,唯一的问题是我不知道如何让 Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject()
调用我的反序列化方法。为了完成这项工作,我需要一些 attribute
或一些特定的方法名称吗?
您需要指定包含合适 TypeNameHandling
值的 JsonSerializerSettings
。这会将 JSON 中的完全限定名称嵌入到 $type
属性 中,然后可以使用它来反序列化。这是一个完整的例子:
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();
}
}
输出,显示 JSON 以及 Root
中的接口属性已被适当反序列化的事实:
{"$type":"Root, Test","First":{"$type":"Foo1, Test","Name":"Fred"},"Second":{"$type":"Foo2, Test","Value":10}}
Method in Foo1
Method in Foo2
您可能想使用其他 TypeNameHandling
值来代替 All
- 有关详细信息,请参阅 the documentation。
为了完整性,将@Daisy 对 Hangfire
的回答应用到您的 StartUp
(这是针对 .NET Core
):
app.UseHangfireDashboard("/hangfire", ...);
app.UseHangfireServer(...);
JobHelper.SetSerializerSettings(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
对于 .Net 核心
GlobalConfiguration.Configuration.UseSerializerSettings(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
})
我正在使用 Hangfire
到 运行 我的自定义 interface
的多个任务,我们称之为 IType
。但是,由于 Hangfire
序列化该方法,从而破坏了该类型的实例,因此当它尝试调用它时,我收到如下错误消息:
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(...
我认为解决这个问题的一种方法是让作为我的 interface
实例的每个 class
可以存储它的 fully qualified domain name
,然后我可以使用 reflection
来return 它是它需要的类型,唯一的问题是我不知道如何让 Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewObject()
调用我的反序列化方法。为了完成这项工作,我需要一些 attribute
或一些特定的方法名称吗?
您需要指定包含合适 TypeNameHandling
值的 JsonSerializerSettings
。这会将 JSON 中的完全限定名称嵌入到 $type
属性 中,然后可以使用它来反序列化。这是一个完整的例子:
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();
}
}
输出,显示 JSON 以及 Root
中的接口属性已被适当反序列化的事实:
{"$type":"Root, Test","First":{"$type":"Foo1, Test","Name":"Fred"},"Second":{"$type":"Foo2, Test","Value":10}}
Method in Foo1
Method in Foo2
您可能想使用其他 TypeNameHandling
值来代替 All
- 有关详细信息,请参阅 the documentation。
为了完整性,将@Daisy 对 Hangfire
的回答应用到您的 StartUp
(这是针对 .NET Core
):
app.UseHangfireDashboard("/hangfire", ...);
app.UseHangfireServer(...);
JobHelper.SetSerializerSettings(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
});
对于 .Net 核心
GlobalConfiguration.Configuration.UseSerializerSettings(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All
})