这是我到现在为止所拥有的。感谢Brian Rodgers:
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;
}
}
我的问题:
需要将默认值设置为“不可为空”字段,而不是“异常”或整个对象为空。缺少值不是问题(通过DefaultContractResolver
提供默认值),但是当在json中将不可为空的值显式设置为null时,这会产生异常。
我上面的代码很接近,但是不够接近。我想我需要找到一种方法来知道json中的值实际上为空,并为这些情况设置ShouldDeserialize =false
。
答案 0 :(得分:2)
您想要的是,在反序列化期间,当为不可为空的成员遇到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
decorator来映射传入的{{ 1}}值恢复为以前的默认值(不能为null,因为基础类型不能为null)。
不必覆盖JsonProperty.ShouldDeserialize
。此谓词允许根据目标对象的状态动态忽略JSON属性值。甚至不会传递反序列化的JSON值。
如果您的类型使用parameterized constructor,则可能还需要覆盖DefaultContractResolver.CreatePropertyFromConstructorParameter
。
您可能想cache the contract resolver以获得最佳性能。
工作示例.Net小提琴here。