【问题标题】:JsonConverter Attribute : deserialize using custom constructor and AutofacJsonConverter 属性:使用自定义构造函数和 Autofac 反序列化
【发布时间】:2016-12-05 20:39:41
【问题描述】:

我正在使用自定义 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!

我有一个提示,即在调用 JsonConverter 时永远不会调用 AutoFac。我什至尝试过属性注入来显式构造 QueryObjectConstruct,但即使这样也从未被调用过。我怎样才能让它工作,以便通过 DI 注入我的 QueryObjectCretor?

我找到了 this 关于依赖注入和 JSON.net 反序列化的文章。但是,这是使用 DeserializeObject() 调用进行手动解析,如果可行,我该如何使其与 JsonConverter 属性一起使用?

谢谢

【问题讨论】:

标签: c# dependency-injection json.net autofac


【解决方案1】:

您可以执行以下步骤来实现您的目标:

  1. 为你的ICustomObjectCreator接口创建一个非泛型接口,让创建对象更加方便。
  2. 引入一个通用的ObjectCreatorBase&lt;T&gt; 基类,它调用您的通用Create 方法。
  3. 创建和设置JsonConvert 使用的默认设置。
  4. 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)。

要使其正常工作,您可以执行以下操作:

  1. CustomConverter 注册为通用。
  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

这是示例的.NET Fiddle link

【讨论】:

    【解决方案2】:

    假设一个 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;
        }
    }
    

    那么,假设你的班级是这样的:

    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查看更多详情

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-03
      • 2022-06-11
      • 2021-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多