![](/img/trans.png)
[英]System.Text.Json: when deserializing, how can I ignore a null or empty string value for a property that has a default value set in the constructor?
[英]Using a custom ContractResolver, how to set a default value instead of null when deserializing a null JSON property to a value-type member?
这是我到目前为止所得到的。 感谢布赖恩罗杰斯:
public class JsonSerializeTest
{
[Fact]
public void deserialize_test()
{
var settings = new JsonSerializerSettings { ContractResolver = new CustomContractResolver() };
var jsonString = "{\"PropertyA\":\"Test\",\"PropertyB\":null}";
var jsonObject = JsonConvert.DeserializeObject<NoConfigModel>(jsonString, settings);
Assert.NotNull(jsonObject);
}
}
public class NoConfigModel
{
public string PropertyA { get; set; }
public int PropertyB { get; set; }
public bool PropertyC { get; set; }
}
class CustomContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
property.ShouldDeserialize = instance =>
{
try
{
PropertyInfo prop = (PropertyInfo)member;
if (prop.CanRead)
{
var value = prop.GetValue(instance, null);// getting default value(0) here instead of null for PropertyB
return value != null;
}
}
catch
{
}
return false;
};
return property;
}
}
我的问题:
需要将默认值设置为 Not Nullable 字段而不是 Exception 或整个对象为 null。 缺少值不是问题(通过DefaultContractResolver
给出默认值),但是当在 json 中将不可为空的值显式设置为 null 时,这会产生异常。
我上面的代码很接近但还不够接近。 我想我需要找到一种方法来知道该值实际上是来自 json 的 null 并为这些情况设置ShouldDeserialize =false
。
您想要的是,在反序列化期间,当遇到不可为空成员的null
值时,将默认(非空)值设置回包含对象中。 这可以通过如下覆盖DefaultContractResolver.CreateProperty
来完成:
class CustomContractResolver : DefaultContractResolver
{
class NullToDefaultValueProvider : ValueProviderDecorator
{
readonly object defaultValue;
public NullToDefaultValueProvider(IValueProvider baseProvider, object defaultValue) : base(baseProvider)
{
this.defaultValue = defaultValue;
}
public override void SetValue(object target, object value)
{
base.SetValue(target, value ?? defaultValue);
}
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property != null && property.PropertyType.IsValueType && Nullable.GetUnderlyingType(property.PropertyType) == null && property.Writable)
{
var defaultValue = property.DefaultValue ?? Activator.CreateInstance(property.PropertyType);
// When a null value is encountered in the JSON we want to set a default value in the class.
property.PropertyType = typeof(Nullable<>).MakeGenericType(new[] { property.PropertyType });
property.ValueProvider = new NullToDefaultValueProvider(property.ValueProvider, defaultValue);
// Remember that the underlying property is actually not nullable so GetValue() will never return null.
// Thus the below just overrides JsonSerializerSettings.NullValueHandling to force the value to be set
// (to the default) even when null is encountered.
property.NullValueHandling = NullValueHandling.Include;
}
return property;
}
// As of 7.0.1, Json.NET suggests using a static instance for "stateless" contract resolvers, for performance reasons.
// http://www.newtonsoft.com/json/help/html/ContractResolver.htm
// http://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_Serialization_DefaultContractResolver__ctor_1.htm
// "Use the parameterless constructor and cache instances of the contract resolver within your application for optimal performance."
// See also https://stackoverflow.com/questions/33557737/does-json-net-cache-types-serialization-information
static CustomContractResolver instance;
// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
static CustomContractResolver() { instance = new CustomContractResolver(); }
public static CustomContractResolver Instance { get { return instance; } }
}
public abstract class ValueProviderDecorator : IValueProvider
{
readonly IValueProvider baseProvider;
public ValueProviderDecorator(IValueProvider baseProvider)
{
if (baseProvider == null)
throw new ArgumentNullException();
this.baseProvider = baseProvider;
}
public virtual object GetValue(object target) { return baseProvider.GetValue(target); }
public virtual void SetValue(object target, object value) { baseProvider.SetValue(target, value); }
}
笔记:
合约解析器通过将返回不可为空值的属性类型更改为相应的Nullable<T>
类型,然后创建一个ValueProvider
装饰器,将传入的null
值映射到前一个默认值(它不能为空,因为底层类型不可为空)。
没有必要覆盖JsonProperty.ShouldDeserialize
。 此谓词允许根据目标对象的状态动态忽略 JSON 属性值。 它甚至没有传递反序列化的 JSON 值。
如果您的类型使用参数化构造函数,您可能还需要覆盖DefaultContractResolver.CreatePropertyFromConstructorParameter
。
您可能希望缓存合同解析器以获得最佳性能。
工作示例 .Net fiddle在这里。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.