【问题标题】:Gain effects of `JsonConstructor` without editing class无需编辑类即可获得 `JsonConstructor` 的效果
【发布时间】:2020-11-19 23:50:16
【问题描述】:

我有一些具有只读属性和构造函数的类,例如:

public class MyClass
{
    public string Foo { get; }
    public bool Bar { get; }
    public MyClass(string foo);
    public MyClass(string foo, bool bar);
}

这将序列化,但 DeserializeObject 将失败。

Newtonsoft.Json.JsonSerializationException:找不到用于 MyClass 类型的构造函数。一个类应该有一个默认构造函数、一个带参数的构造函数或一个标有 JsonConstructor 属性的构造函数。路径“...”,第 1 行,位置 1

添加JsonConstructor 属性会起作用,但有时我无法控制类型源代码。我可以告诉转换器像属性有其他方式一样吗?

理想情况下,无需在JsonConverter.ReadJson 中为每种相关类型手动执行每个参数。

【问题讨论】:

  • 我没有您的问题的答案,但作为一种解决方法 - 没有什么会迫使您反序列化为该特定类型。你不妨反序列化为自己的类型,甚至是匿名类型,然后将结果映射到实际的目标类型。当然,这需要编写一些代码,但自定义 json 转换器也需要编写代码......
  • 嗯,这与为每种类型执行每个参数非常相似......我可能可以通过反射编写一个通用解决方案(无论如何我假设 Json.Net 在内部做了什么)。但是如果可以说“使用这个构造函数typeA, typeB”或者“假装它在成员y上有这个属性x”宁愿不重新发明。

标签: c# json.net


【解决方案1】:

做了一些研究,看起来您可以在反序列化期间使用自定义JsonContractResolver,这将为序列化程序提供正确的对象创建者。
所以,反序列化看起来像:

var settings = new JsonSerializerSettings { ContractResolver = new MyContractResolver() };
var myClass = JsonConvert.DeserializeObject<MyClass>("{ \"Foo\": \"Hello\", \"Bar\": true }", settings);

MyContractResolver 是:

public class MyContractResolver : DefaultContractResolver
{
    public override JsonContract ResolveContract(Type type)
    {
        var contract = base.ResolveContract(type);
        if (type == typeof(MyClass) && contract is JsonObjectContract objectContract)
        {
            objectContract.OverrideCreator = (args) => new MyClass((string)args[0], (bool)args[1]);
            var overrideConstructor = typeof(MyClass).GetConstructor(new[] { typeof(string), typeof(bool) });
            foreach (var param in CreateConstructorParameters(overrideConstructor, objectContract.Properties))
                objectContract.CreatorParameters.Add(param);
        }
        return contract;
    }
}

当然,这是非常示意性的,JsonSerializer 属性使用起来要方便得多。但是,如果您有遗留类,这可能会有所帮助。

想法是,当 Serializer 为您的类型填充 JsonContract 时 - 让默认合约解析器构建它,然后注入您实际需要在合约的 OverrideCreator 属性中调用的构造函数,并在 @987654329 中描述其参数@。

【讨论】:

  • 似乎是下注的方法。环顾该代码,有一个可覆盖的CreateObjectContract。它实际上使用GetAttributeConstructor 来查找属性,但它似乎不可覆盖,似乎DefaultContractResolver 处理了一堆属性。
猜你喜欢
  • 2015-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-23
  • 1970-01-01
相关资源
最近更新 更多