JsonConverter 属性:使用自定义构造函数和 Autofac 反序列化
JsonConverter Attribute : deserialize using custom constructor and Autofac
我正在使用自定义 JsonConverter 来转换我的 JSON 对象。这是通过下面的 IQuery 对象的 JsonConverter 属性实现的
[JsonConverter(typeof(CustomConverter<IQuery>))]
public interface IQuery
{
}
下面是自定义泛型 class(为简洁起见,删除了一些位)
public class CustomConverter<T> : JsonConverter
{
// This should be created via AutoFac
public ICustomObjectCreator<T> ObjectCreator { get; set; }
// This default constructr always gets called
public CustomConverter() {}
// I want to call this constructor
[JsonConstructor]
public CustomConverter(ICustomObjectCreator<T> objectCreator)
{
Context = context;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
protected T Create(Type objectType, JObject jObject)
{
var type = jObject.GetValue("type", StringComparison.OrdinalIgnoreCase)?.Value<string>();
return ObjectCreator.Create(type);
}
}
ICustomObjectConverter 接口很简单
public interface ICustomObjectCreator<out T>
{
T Create(string type);
}
及其实现之一
public class QueryObjectCreator : ICustomObjectCreator<IQuery>
{
public IQuery Create(string type)
{
// ... some logic to create a concrete object
return (IQuery)concreteObject;
}
}
最后,Autofac 被连线以遵守上述内容
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
问题:
- 调用CustomJsonConverter时,只调用其默认构造函数。永远不会调用 JsonConstructor。
- 如果我删除默认构造函数,则永远不会调用整个 JsonConverter!
我知道调用 JsonConverter 时从未调用 AutoFac。我什至尝试 属性 注入来显式构造 QueryObjectConstruct,但即使这样也从未调用过。我怎样才能让它工作,以便通过 DI 注入我的 QueryObjectCretor?
我找到了 this 关于依赖注入和 JSON.net 反序列化的文章。但是,这是使用 DeserializeObject<>() 调用的手动解析,如果它有效,我如何才能使其与 JsonConverter 属性一起使用?
谢谢
您可以执行以下步骤来实现您的目标:
- 为您的
ICustomObjectCreator
接口创建一个非通用接口,以便更方便地创建对象。
- 引入一个通用的
ObjectCreatorBase<T>
基础 class,它调用您的通用 Create
方法。
- 创建和设置
JsonConvert
使用的默认设置。
- 将
AutofacContractResolver
设置为 ContractResolver
。
请参阅以下示例以帮助您入门:
void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
var container = builder.Build();
Func<JsonSerializerSettings> settingsFactory = () =>
{
var settings = new JsonSerializerSettings();
settings.ContractResolver = new AutofacContractResolver(container);
return settings;
};
JsonConvert.DefaultSettings = settingsFactory;
var myObject = new MyObject { Query = new Query(42) };
var json = JsonConvert.SerializeObject(myObject);
myObject = JsonConvert.DeserializeObject<MyObject>(json);
Console.WriteLine(myObject.Query.MyProperty);
}
// Define other methods and classes here
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
var customObjectCreatorType = typeof(ICustomObjectCreator<>).MakeGenericType(objectType);
if (!_container.IsRegistered(customObjectCreatorType))
return contract;
var customObjectCreator = (ICustomObjectCreator) _container.Resolve(customObjectCreatorType);
// I don't know how you want to obtain the string which shall be passed to CreateObject
contract.DefaultCreator = () => customObjectCreator.CreateObject("XYZ");
return contract;
}
}
public interface ICustomObjectCreator
{
object CreateObject(string type);
}
public interface ICustomObjectCreator<out T> : ICustomObjectCreator
{
T Create(string type);
}
public abstract class ObjectCreatorBase<T> : ICustomObjectCreator<T>
{
public object CreateObject(string type)
{
return Create(type);
}
public abstract T Create(string type);
}
public class QueryObjectCreator : ObjectCreatorBase<IQuery>
{
public override IQuery Create(string type)
{
Console.WriteLine("Create called");
// ... some logic to create a concrete object
var concreteObject = new Query();
return (IQuery)concreteObject;
}
}
public interface IQuery
{
int MyProperty { get; set; }
}
public class Query : IQuery
{
public int MyProperty { get; set; }
public Query()
{
}
public Query(int myProperty)
{
MyProperty = myProperty;
}
}
public class MyObject
{
public IQuery Query { get; set; }
}
输出应该是
Create called
42
也许您可以通过简单地使用 Autofac 直接创建对象来删除所有 ICustomObjectCreator
实例来简化代码。
更新
第一种方法可行,但它没有考虑到您需要获取一个字符串来决定您正在创建哪种对象 (type
)。
要使其正常工作,您可以执行以下操作:
- 将
CustomConverter
注册为通用。
- 将
ResolveContractConverter
方法覆盖为 return 转换器实例,以防任何 ICustomObjectCreator
类型已注册。
- 更改
DefaultSettings
以便使用 AutofacContractResolver
。
参见以下示例:
void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(CustomConverter<>)).AsSelf().InstancePerLifetimeScope();
var container = builder.Build();
Func<JsonSerializerSettings> settingsFactory = () =>
{
var settings = new JsonSerializerSettings();
settings.ContractResolver = new AutofacContractResolver(container);
return settings;
};
JsonConvert.DefaultSettings = settingsFactory;
var myObject = new MyObject { Query = new Query(42) };
var json = JsonConvert.SerializeObject(myObject);
myObject = JsonConvert.DeserializeObject<MyObject>(json);
Console.WriteLine(myObject.Query.MyProperty);
}
// Define other methods and classes here
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonConverter ResolveContractConverter(Type objectType)
{
var customObjectCreatorType = typeof(ICustomObjectCreator<>).MakeGenericType(objectType);
if (!_container.IsRegistered(customObjectCreatorType))
return base.ResolveContractConverter(objectType);
var customConverterType = typeof(CustomConverter<>).MakeGenericType(objectType);
return (JsonConverter) _container.Resolve(customConverterType);
}
}
public class CustomConverter<T> : JsonConverter
{
// This should be created via AutoFac
public ICustomObjectCreator<T> ObjectCreator { get; }
// This default constructr always gets called
public CustomConverter() { }
// I want to call this constructor
public CustomConverter(ICustomObjectCreator<T> objectCreator)
{
Console.WriteLine("Constructor called");
ObjectCreator = objectCreator;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
protected T Create(Type objectType, JObject jObject)
{
var type = jObject.GetValue("type", StringComparison.OrdinalIgnoreCase)?.Value<string>();
return ObjectCreator.Create(type);
}
}
public interface ICustomObjectCreator<out T>
{
T Create(string type);
}
public class QueryObjectCreator : ICustomObjectCreator<IQuery>
{
public IQuery Create(string type)
{
Console.WriteLine("Create called");
// ... some logic to create a concrete object
var concreteObject = new Query();
return (IQuery)concreteObject;
}
}
public interface IQuery
{
int MyProperty { get; set; }
}
public class Query : IQuery
{
public int MyProperty { get; set; }
public Query()
{
}
public Query(int myProperty)
{
MyProperty = myProperty;
}
}
public class MyObject
{
public IQuery Query { get; set; }
}
输出应该是
Constructor called
Create called
42
这是示例的 .NET Fiddle link。
假设 autofac 设置如下:
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
// use Autofac to create types that have been registered with it
if (_container.IsRegistered(objectType))
{
contract.DefaultCreator = () => _container.Resolve(objectType);
}
return contract;
}
}
那么,假设你的class是这样的:
public class TaskController
{
private readonly ITaskRepository _repository;
private readonly ILogger _logger;
public TaskController(ITaskRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public ITaskRepository Repository
{
get { return _repository; }
}
public ILogger Logger
{
get { return _logger; }
}
}
因此,解析器在反序列化中的用法可能是这样的:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();
IContainer container = builder.Build();
AutofacContractResolver contractResolver = new AutofacContractResolver(container);
string json = @"{
'Logger': {
'Level':'Debug'
}
}";
// ITaskRespository and ILogger constructor parameters are injected by Autofac
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
Console.WriteLine(controller.Repository.GetType().Name);
您可以在http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
中查看更多详细信息
我正在使用自定义 JsonConverter 来转换我的 JSON 对象。这是通过下面的 IQuery 对象的 JsonConverter 属性实现的
[JsonConverter(typeof(CustomConverter<IQuery>))]
public interface IQuery
{
}
下面是自定义泛型 class(为简洁起见,删除了一些位)
public class CustomConverter<T> : JsonConverter
{
// This should be created via AutoFac
public ICustomObjectCreator<T> ObjectCreator { get; set; }
// This default constructr always gets called
public CustomConverter() {}
// I want to call this constructor
[JsonConstructor]
public CustomConverter(ICustomObjectCreator<T> objectCreator)
{
Context = context;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
protected T Create(Type objectType, JObject jObject)
{
var type = jObject.GetValue("type", StringComparison.OrdinalIgnoreCase)?.Value<string>();
return ObjectCreator.Create(type);
}
}
ICustomObjectConverter 接口很简单
public interface ICustomObjectCreator<out T>
{
T Create(string type);
}
及其实现之一
public class QueryObjectCreator : ICustomObjectCreator<IQuery>
{
public IQuery Create(string type)
{
// ... some logic to create a concrete object
return (IQuery)concreteObject;
}
}
最后,Autofac 被连线以遵守上述内容
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
问题:
- 调用CustomJsonConverter时,只调用其默认构造函数。永远不会调用 JsonConstructor。
- 如果我删除默认构造函数,则永远不会调用整个 JsonConverter!
我知道调用 JsonConverter 时从未调用 AutoFac。我什至尝试 属性 注入来显式构造 QueryObjectConstruct,但即使这样也从未调用过。我怎样才能让它工作,以便通过 DI 注入我的 QueryObjectCretor?
我找到了 this 关于依赖注入和 JSON.net 反序列化的文章。但是,这是使用 DeserializeObject<>() 调用的手动解析,如果它有效,我如何才能使其与 JsonConverter 属性一起使用?
谢谢
您可以执行以下步骤来实现您的目标:
- 为您的
ICustomObjectCreator
接口创建一个非通用接口,以便更方便地创建对象。 - 引入一个通用的
ObjectCreatorBase<T>
基础 class,它调用您的通用Create
方法。 - 创建和设置
JsonConvert
使用的默认设置。 - 将
AutofacContractResolver
设置为ContractResolver
。
请参阅以下示例以帮助您入门:
void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
var container = builder.Build();
Func<JsonSerializerSettings> settingsFactory = () =>
{
var settings = new JsonSerializerSettings();
settings.ContractResolver = new AutofacContractResolver(container);
return settings;
};
JsonConvert.DefaultSettings = settingsFactory;
var myObject = new MyObject { Query = new Query(42) };
var json = JsonConvert.SerializeObject(myObject);
myObject = JsonConvert.DeserializeObject<MyObject>(json);
Console.WriteLine(myObject.Query.MyProperty);
}
// Define other methods and classes here
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
var customObjectCreatorType = typeof(ICustomObjectCreator<>).MakeGenericType(objectType);
if (!_container.IsRegistered(customObjectCreatorType))
return contract;
var customObjectCreator = (ICustomObjectCreator) _container.Resolve(customObjectCreatorType);
// I don't know how you want to obtain the string which shall be passed to CreateObject
contract.DefaultCreator = () => customObjectCreator.CreateObject("XYZ");
return contract;
}
}
public interface ICustomObjectCreator
{
object CreateObject(string type);
}
public interface ICustomObjectCreator<out T> : ICustomObjectCreator
{
T Create(string type);
}
public abstract class ObjectCreatorBase<T> : ICustomObjectCreator<T>
{
public object CreateObject(string type)
{
return Create(type);
}
public abstract T Create(string type);
}
public class QueryObjectCreator : ObjectCreatorBase<IQuery>
{
public override IQuery Create(string type)
{
Console.WriteLine("Create called");
// ... some logic to create a concrete object
var concreteObject = new Query();
return (IQuery)concreteObject;
}
}
public interface IQuery
{
int MyProperty { get; set; }
}
public class Query : IQuery
{
public int MyProperty { get; set; }
public Query()
{
}
public Query(int myProperty)
{
MyProperty = myProperty;
}
}
public class MyObject
{
public IQuery Query { get; set; }
}
输出应该是
Create called
42
也许您可以通过简单地使用 Autofac 直接创建对象来删除所有 ICustomObjectCreator
实例来简化代码。
更新
第一种方法可行,但它没有考虑到您需要获取一个字符串来决定您正在创建哪种对象 (type
)。
要使其正常工作,您可以执行以下操作:
- 将
CustomConverter
注册为通用。 - 将
ResolveContractConverter
方法覆盖为 return 转换器实例,以防任何ICustomObjectCreator
类型已注册。 - 更改
DefaultSettings
以便使用AutofacContractResolver
。
参见以下示例:
void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<QueryObjectCreator>()
.As<ICustomObjectCreator<IQuery>>()
.InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(CustomConverter<>)).AsSelf().InstancePerLifetimeScope();
var container = builder.Build();
Func<JsonSerializerSettings> settingsFactory = () =>
{
var settings = new JsonSerializerSettings();
settings.ContractResolver = new AutofacContractResolver(container);
return settings;
};
JsonConvert.DefaultSettings = settingsFactory;
var myObject = new MyObject { Query = new Query(42) };
var json = JsonConvert.SerializeObject(myObject);
myObject = JsonConvert.DeserializeObject<MyObject>(json);
Console.WriteLine(myObject.Query.MyProperty);
}
// Define other methods and classes here
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonConverter ResolveContractConverter(Type objectType)
{
var customObjectCreatorType = typeof(ICustomObjectCreator<>).MakeGenericType(objectType);
if (!_container.IsRegistered(customObjectCreatorType))
return base.ResolveContractConverter(objectType);
var customConverterType = typeof(CustomConverter<>).MakeGenericType(objectType);
return (JsonConverter) _container.Resolve(customConverterType);
}
}
public class CustomConverter<T> : JsonConverter
{
// This should be created via AutoFac
public ICustomObjectCreator<T> ObjectCreator { get; }
// This default constructr always gets called
public CustomConverter() { }
// I want to call this constructor
public CustomConverter(ICustomObjectCreator<T> objectCreator)
{
Console.WriteLine("Constructor called");
ObjectCreator = objectCreator;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
// Load JObject from stream
var jObject = JObject.Load(reader);
// Create target object based on JObject
var target = Create(objectType, jObject);
// Populate the object properties
serializer.Populate(jObject.CreateReader(), target);
return target;
}
protected T Create(Type objectType, JObject jObject)
{
var type = jObject.GetValue("type", StringComparison.OrdinalIgnoreCase)?.Value<string>();
return ObjectCreator.Create(type);
}
}
public interface ICustomObjectCreator<out T>
{
T Create(string type);
}
public class QueryObjectCreator : ICustomObjectCreator<IQuery>
{
public IQuery Create(string type)
{
Console.WriteLine("Create called");
// ... some logic to create a concrete object
var concreteObject = new Query();
return (IQuery)concreteObject;
}
}
public interface IQuery
{
int MyProperty { get; set; }
}
public class Query : IQuery
{
public int MyProperty { get; set; }
public Query()
{
}
public Query(int myProperty)
{
MyProperty = myProperty;
}
}
public class MyObject
{
public IQuery Query { get; set; }
}
输出应该是
Constructor called
Create called
42
这是示例的 .NET Fiddle link。
假设 autofac 设置如下:
public class AutofacContractResolver : DefaultContractResolver
{
private readonly IContainer _container;
public AutofacContractResolver(IContainer container)
{
_container = container;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
// use Autofac to create types that have been registered with it
if (_container.IsRegistered(objectType))
{
contract.DefaultCreator = () => _container.Resolve(objectType);
}
return contract;
}
}
那么,假设你的class是这样的:
public class TaskController
{
private readonly ITaskRepository _repository;
private readonly ILogger _logger;
public TaskController(ITaskRepository repository, ILogger logger)
{
_repository = repository;
_logger = logger;
}
public ITaskRepository Repository
{
get { return _repository; }
}
public ILogger Logger
{
get { return _logger; }
}
}
因此,解析器在反序列化中的用法可能是这样的:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();
IContainer container = builder.Build();
AutofacContractResolver contractResolver = new AutofacContractResolver(container);
string json = @"{
'Logger': {
'Level':'Debug'
}
}";
// ITaskRespository and ILogger constructor parameters are injected by Autofac
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
ContractResolver = contractResolver
});
Console.WriteLine(controller.Repository.GetType().Name);
您可以在http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
中查看更多详细信息