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

时间:2016-08-11 13:44:57

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

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

下面的IQuery对象实现的
[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!
  3. 我有一个inklinkg,在调用JsonConverter时从不调用AutoFac。我甚至尝试了属性注入来显式构造QueryObjectConstruct,但即使是从未调用过。如何让它工作,以便我的QueryObjectCretor通过DI注入?

    我找到了关于依赖注入和JSON.net反序列化的this文章。但是,这是使用DeserializeObject&lt;&gt;()调用进行手动解析,如果它可以工作,我怎么能使它与JsonConverter属性一起使用?

    由于

1 个答案:

答案 0 :(得分:1)

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

  1. ICustomObjectCreator界面创建非通用界面,以便更方便地创建对象。
  2. 介绍一个通用ObjectCreatorBase<T>基类,它调用您的通用Create方法。
  3. 创建并设置JsonConvert使用的默认设置。
  4. AutofacContractResolver设为ContractResolver
  5. 请参阅以下示例以帮助您入门:

    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将被使用。
    4. 请参阅以下示例:

      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