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

2023-12-12

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

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

Thanks


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

  1. 为您创建一个非通用接口ICustomObjectCreator接口,使创建对象更加方便。
  2. 引入一个通用的ObjectCreatorBase<T>调用您的泛型的基类Create method.
  3. 创建并设置默认设置JsonConvert.
  4. Set the AutofacContractResolver as 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

也许你可以通过删除所有来简化代码ICustomObjectCreator只需使用 Autofac 直接创建对象即可实例化。

Update

第一种方法有效,但它没有考虑到您需要获取一个字符串来决定您要创建哪种类型的对象(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 链接对于样品。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JsonConverter 属性:使用自定义构造函数和 Autofac 反序列化 的相关文章

  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • 使用Physics.Raycast 和Physics2D.Raycast 检测对象上的点击

    我的场景中有一个空的游戏对象 带有 2D 组件盒碰撞器 我将脚本附加到该游戏对象 void OnMouseDown Debug Log clic 但是当我点击我的游戏对象时 没有任何效果 你有什么想法 如何检测我的盒子碰撞器上的点击 使用光
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 互斥体实现可以互换(独立于线程实现)

    所有互斥体实现最终都会调用相同的基本系统 硬件调用吗 这意味着它们可以互换吗 具体来说 如果我使用 gnu parallel算法 使用openmp 并且我想让他们称之为线程安全的类我可以使用boost mutex用于锁定 或者我必须编写自己
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 读取文件特定行号的有效方法。 (奖励:Python 手册印刷错误)

    我有一个 100 GB 的文本文件 它是来自数据库的 BCP 转储 当我尝试导入它时BULK INSERT 我在第 219506324 行上收到一个神秘错误 在解决此问题之前 我想看看这一行 但可惜的是我最喜欢的方法 import line
  • 将 System.Windows.Input.KeyEventArgs 键转换为 char

    我需要将事件参数作为char 但是当我尝试转换 Key 枚举时 我得到的字母和符号与传入的字母和符号完全不同 如何正确地将密钥转换为字符 这是我尝试过的 ObserveKeyStroke this new ObervableKeyStrok
  • 存储来自其他程序的事件

    我想将其他应用程序的事件存储在我自己的应用程序中 事件示例 打开 最小化 Word 或打开文件时 这样的事可能吗 运行程序 http msdn microsoft com en us library ms813609 aspx and 打开
  • 用于检查项目文件中的项目变量和引用路径的 api

    我正在研究一个 net application VS2010 与 x 没有 解和变量号这些解决方案中的项目数量 我需要检查项目属性 特定于一定数量的项目 是否同质 并且检查 验证构建期间的参考路径 有没有一个API是这样的吗 如果没有 我该
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • C++:.bmp 到文件中的字节数组

    是的 我已经解决了与此相关的其他问题 但我发现它们没有太大帮助 他们提供了一些帮助 但我仍然有点困惑 所以这是我需要做的 我们有一个 132x65 的屏幕 我有一个 132x65 的 bmp 我想遍历 bmp 并将其分成小的 1x8 列以获
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 将 log4net 与 Autofac 结合使用

    我正在尝试将 log4net 与 Autofac 一起使用 我粘贴了这段代码http autofac readthedocs org en latest examples log4net html http autofac readthed
  • std::async 与重载函数

    可能的重复 std bind 重载解析 https stackoverflow com questions 4159487 stdbind overload resolution 考虑以下 C 示例 class A public int f
  • 有人可以提供一个使用 Amazon Web Services 的 itemsearch 的 C# 示例吗

    我正在尝试使用 Amazon Web Services 查询艺术家和标题信息并接收回专辑封面 使用 C 我找不到任何与此接近的示例 所有在线示例都已过时 并且不适用于 AWS 的较新版本 有一个开源项目CodePlex http www c
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • 线程和 fork()。我该如何处理呢? [复制]

    这个问题在这里已经有答案了 可能的重复 多线程程序中的fork https stackoverflow com questions 1235516 fork in multi threaded program 如果我有一个使用 fork 的
  • 英特尔 Pin 与 C++14

    问题 我有一些关于在 C 14 或其他 C 版本中使用英特尔 Pin 的问题 使用较新版本从较旧的 C 编译代码很少会出现任何问题 但由于 Intel Pin 是操作指令级别的 如果我使用 C 11 或 C 14 编译它 是否会出现任何不良
  • 如何使用 Word Automation 获取页面范围

    如何使用办公自动化找到 Microsoft Word 中第 n 页的范围 似乎没有 getPageRange n 函数 并且不清楚它们是如何划分的 这就是您从 VBA 执行此操作的方法 转换为 Matlab COM 调用应该相当简单 Pub
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐