JsonConverter属性:使用自定义构造函数和Autofac反序列化 [英] JsonConverter Attribute : deserialize using custom constructor and Autofac

查看:67
本文介绍了JsonConverter属性:使用自定义构造函数和Autofac反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用自定义的JsonConverter来转换我的JSON对象.这是通过下面的IQuery对象的JsonConverter属性实现的

Am using a custom JsonConverter to convert my JSON object. This is achieved via the JsonConverter attribute to the IQuery object below

[JsonConverter(typeof(CustomConverter<IQuery>))]
public interface IQuery
{
}

自定义通用类如下(为简洁起见,删除了一些位)

The custom generic class is below (some bits removed for brevity)

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接口很简单

The ICustomObjectConverter interface is simple

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就是为了纪念以上

Finally, Autofac is wired to honor the above

builder.RegisterType<QueryObjectCreator>()
       .As<ICustomObjectCreator<IQuery>>()
       .InstancePerLifetimeScope();

问题:

  1. 调用CustomJsonConverter时,仅调用其默认构造函数.永远不会调用JsonConstructor.
  2. 如果我删除默认构造函数,则永远不会调用整个JsonConverter!

我有一个墨水链接,当调用JsonConverter时,永远不会调用AutoFac.我什至尝试使用属性注入来显式构造QueryObjectConstruct,但即使这样也从未调用过.如何使其工作,以便通过DI注入QueryObjectCretor?

I have an inklinkg that AutoFac is never being called when JsonConverter is being invoked. I even tried property injection to construct QueryObjectConstruct explicitly, but even that is never called. How can I make it work so that my QueryObjectCretor is injected via DI?

我发现这篇文章有关依赖注入和JSON.net反序列化.但是,这是为了使用DeserializeObject<>()调用进行手动解析,如果可以,如何使它与JsonConverter属性一起使用?

I found this article about Dependency Injection and JSON.net deserialization. However, that is for manual resolve using DeserializeObject<>() call, how can I, if it works, make it work with JsonConverter attribute?

谢谢

推荐答案

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

You could do the following steps to achieve your goal:

  1. 为您的ICustomObjectCreator接口创建一个非通用接口,以使创建对象更加方便.
  2. 介绍一个通用的ObjectCreatorBase<T>基类,该基类调用您的通用Create方法.
  3. 创建并设置JsonConvert使用的默认设置.
  4. AutofacContractResolver设置为ContractResolver.
  1. Create a non generic interface for your ICustomObjectCreator interface, to make creating objects more convenient.
  2. Introduce a generic ObjectCreatorBase<T> base class which calls your generic Create method.
  3. Create and set default settings which are used by JsonConvert.
  4. Set the AutofacContractResolver as ContractResolver.

请参见以下示例以开始使用

See the following example to get you started:

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实例来简化代码.

Maybe you could simplify the code by removing all ICustomObjectCreator instances by simply using Autofac to create your objects directly.

第一种方法有效,但是它没有考虑您需要获取字符串来确定要创建的对象的类型(type).

The first approach works, but it does not take into account that you need to get a string to decide which kind of object you are creating (type).

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

To get this to work you could the following:

  1. CustomConverter注册为通用.
  2. 如果已为该类型注册了任何ICustomObjectCreator,则覆盖ResolveContractConverter方法以返回转换器的实例.
  3. 更改DefaultSettings,以便使用AutofacContractResolver.
  1. Register the CustomConverter as generic.
  2. Overwrite the ResolveContractConverter method to return an instance of the converter in case any ICustomObjectCreator has been registered for the type.
  3. Alter the DefaultSettings so that the AutofacContractResolver will be used.

请参见以下示例:

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小提琴链接.

这篇关于JsonConverter属性:使用自定义构造函数和Autofac反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆