C# 当没有相应的值进行反序列化时,如何使用构造函数的默认参数值?
我有一个类,它直接在代码中实例化,也由反序列化程序实例化。通常,ImmutablePropertyStore对象的JSON表示形式中存在的所有构造函数参数都有一个值——在本例中,一切正常。但是,有时JSON表示中可能缺少一些或所有值。在这种情况下,我希望json.NET使用构造函数中提供的默认参数。但是,它似乎忽略了这些默认参数,而是实例化了该类,并将缺少的值设置为该数据类型的默认值—如果是布尔值,则为false 下面的示例代码说明了这个问题(实际上,还有很多属性):C# 当没有相应的值进行反序列化时,如何使用构造函数的默认参数值?,c#,json.net,C#,Json.net,我有一个类,它直接在代码中实例化,也由反序列化程序实例化。通常,ImmutablePropertyStore对象的JSON表示形式中存在的所有构造函数参数都有一个值——在本例中,一切正常。但是,有时JSON表示中可能缺少一些或所有值。在这种情况下,我希望json.NET使用构造函数中提供的默认参数。但是,它似乎忽略了这些默认参数,而是实例化了该类,并将缺少的值设置为该数据类型的默认值—如果是布尔值,则为false 下面的示例代码说明了这个问题(实际上,还有很多属性): 使用系统; 使用Newto
使用系统;
使用Newtonsoft.Json;
公共课程
{
公共静态void Main()
{
ImmutablePropertyStore ImmutablePropertyStore=新的ImmutablePropertyStore();
Console.WriteLine(immutablePropertyStore);
字符串jsonRepresentation=JsonConvert.SerializeObject(immutablePropertyStore,Formatting.Indented);
WriteLine(JsonConvert.DeserializeObject(jsonRepresentation));
//所需输出:属性A为“真”,属性B为“真”。实际输出:属性A为“假”,属性B为“真”。
WriteLine(JsonConvert.DeserializeObject(“{\'PropertyB\':true}”);
//所需输出:属性A为“真”,属性B为“假”。实际输出:属性A为“假”,属性B为“假”。
WriteLine(JsonConvert.DeserializeObject(“{}”);
}
}
公共类ImmutablePropertyStore
{
公共财产{get;}
公共布尔属性b{get;}
公共ImmutablePropertyStore(boolPropertyA=true,boolPropertyB=false)
{
PropertyA=PropertyA;
PropertyB=PropertyB;
}
公共重写字符串ToString()
{
return$“属性A是{PropertyA}”,属性B是{PropertyB}”;
}
}
或者作为.NET小提琴手:
我希望输出的最后一行是:
属性A为“真”,属性B为“假”
而不是:
属性A为“假”,属性B为“假”
我如何通过以下方式实现这一点:
您可以编写一个自定义的
JsonConverter
,或者将ImmutablePropertyStore
的构造函数参数设置为null,并将其默认值设置为null
。然后在ctor中,您可以确定“真实”默认值应该是什么
public ImmutablePropertyStore(bool? propertyA = null, bool? propertyB = null)
{
PropertyA = propertyA ?? true;
PropertyB = propertyB ?? false;
}
正如您所提到的,将使用属性类型的默认值(null
用于可为空的类型),然后将其转换为ctor中正确的“实”值
使用此方法时,默认值不太明显,但我不确定这是否会成为问题。我不知道您的完整用例,这是否与您正在寻找的一样?将
DefaultValue
属性与JsonSerializerSettings
结合使用
使用系统;
使用系统组件模型;
使用Newtonsoft.Json;
公共课程
{
公共静态void Main()
{
JsonConvert.DefaultSettings=()=>新的JsonSerializerSettings
{
DefaultValueHandling=DefaultValueHandling.Populate
};
WriteLine(JsonConvert.DeserializeObject(“{}”);
WriteLine(JsonConvert.DeserializeObject(@“{propertyB:“true”}”);
WriteLine(JsonConvert.DeserializeObject(@“{PropertyA:“false”,PropertyB:“true”}”);
}
}
公共类ImmutablePropertyStore
{
[默认值(真)]
公共财产{get;}
[默认值(false)]
公共布尔属性b{get;}
公共ImmutablePropertyStore(bool propertyA、bool propertyB)
{
PropertyA=PropertyA;
PropertyB=PropertyB;
}
公共重写字符串ToString()
{
return$“属性A是{PropertyA}”,属性B是{PropertyB}”;
}
}
输出:
Property A is 'True' and Property B is 'False'.
Property A is 'True' and Property B is 'True'.
Property A is 'False' and Property B is 'True'.
我看到这些问题,
- 在这方面,使类不可变在某种程度上是一种编码练习,因为类不是密封的,如果使用者想要获取对象并对其进行更改,那么这是可以做到的
- 现在只有两个默认值,但如果有更多的默认值呢?这一切的复杂性是指数增长的
回答 我建议您更改设计,使一个基类的所有属性都是可变的,这将是任何反序列化操作的目标(因为可变属性可以很好地进行反序列化)。然后使用者将通过covert/copy/reflect从该基类获取不可变实例
var bse = JsonConvert.DeserializeObject<MutablePropertyStore>("{ 'PropertyB' : true }");
Console.WriteLine("Base: " + bse.ToString());
var derived = new ImmutablePropertyStore(bse);
Console.WriteLine("Derived: " + derived.ToString());
范例 代码
public sealed class ImmutablePropertyStore : MutablePropertyStore
{
public new bool PropertyA { get; private set; }
public new bool PropertyB { get; private set; }
public ImmutablePropertyStore() { }
public ImmutablePropertyStore(MutablePropertyStore ms)
{
PropertyA = ms.PropertyA;
PropertyB = ms.PropertyB;
}
public ImmutablePropertyStore(bool propertyA = true, bool propertyB = false)
{
PropertyA = propertyA;
PropertyB = propertyB;
}
public override string ToString()
=> $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";
}
public class MutablePropertyStore
{
public virtual bool PropertyA { get; set;}
public virtual bool PropertyB { get; set;}
// Set all defaults here
public MutablePropertyStore() { PropertyA = true; }
public override string ToString()
=> $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";
}
这似乎是在缺少某些值时唯一有效的答案,但并不完全感谢您的建议。不幸的是,这将阻止使用任何缺少的参数直接实例化ImmutablePropertyStore(“例如,新ImmutablePropertyStore()”将不再编译)。在我的用例中,我需要将默认值用于直接实例化和通过反序列化进行实例化。我认为这个解决方案对于我的特殊情况来说可能是过分了——不过,密封类可能是个好主意。此外,我建议复杂性(取决于它的定义)保持不变,并且随着添加更多属性,类的大小将线性增长。没有什么东西会成倍增长。
Base: Property A is 'True' and Property B is 'True'.
Derived: Property A is 'True' and Property B is 'True'.
public sealed class ImmutablePropertyStore : MutablePropertyStore
{
public new bool PropertyA { get; private set; }
public new bool PropertyB { get; private set; }
public ImmutablePropertyStore() { }
public ImmutablePropertyStore(MutablePropertyStore ms)
{
PropertyA = ms.PropertyA;
PropertyB = ms.PropertyB;
}
public ImmutablePropertyStore(bool propertyA = true, bool propertyB = false)
{
PropertyA = propertyA;
PropertyB = propertyB;
}
public override string ToString()
=> $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";
}
public class MutablePropertyStore
{
public virtual bool PropertyA { get; set;}
public virtual bool PropertyB { get; set;}
// Set all defaults here
public MutablePropertyStore() { PropertyA = true; }
public override string ToString()
=> $"Property A is '{PropertyA}' and Property B is '{PropertyB}'.";
}