JsonConverter属性:使用自定义构造函数和autofacc进行反序列化

本文关键字:autofacc 反序列化 构造函数 自定义 属性 JsonConverter | 更新日期: 2023-09-27 18:02:11

我使用自定义JsonConverter来转换我的JSON对象。这是通过

下面的IQuery对象的JsonConverter属性实现的
[JsonConverter(typeof(CustomConverter<IQuery>))]
public interface IQuery
{
}

自定义泛型类如下(为了简洁,删除了一些位)

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();

问题:

  1. 当CustomJsonConverter被调用时,只调用它的默认构造函数。JsonConstructor永远不会被调用。
  2. 如果我删除默认构造函数,那么整个JsonConverter永远不会被调用!

我有一个inklinkg AutoFac从来没有被调用时,JsonConverter被调用。我甚至尝试了属性注入来显式地构造QueryObjectConstruct,但即使这样也从未调用过。我怎样才能使它工作,使我的QueryObjectCretor通过DI注入?

我找到了一篇关于依赖注入和JSON.net反序列化的文章。然而,这是手动解析使用DeserializeObject<>()调用,我怎么能,如果它的工作,使它与JsonConverter属性工作?

谢谢

JsonConverter属性:使用自定义构造函数和autofacc进行反序列化

您可以按照以下步骤实现您的目标:

  1. 为你的ICustomObjectCreator接口创建一个非通用接口,使创建对象更方便。
  2. 引入一个通用的ObjectCreatorBase<T>基类来调用通用的Create方法。
  3. 创建并设置JsonConvert使用的默认设置。
  4. 设置AutofacContractResolverContractResolver

请看下面的例子来帮助你开始:

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)。

要使其工作,可以执行以下命令:

  1. 注册CustomConverter为generic
  2. 覆盖ResolveContractConverter方法,以在任何ICustomObjectCreator已注册为该类型时返回转换器的实例。
  3. 修改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

假设有如下自动设置:

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;
    }
}

然后,假设你的类是这样的:

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

查看更多详细信息