在JSON.NET中序列化null

当通过JSON.NET序列化任意数据时,任何属性为null的属性将被写入JSON

“propertyName”:null

当然这是正确的。

不过,我有一个要求,自动将所有空值转换为默认的空值,例如空string s应该成为String.Empty ,null int? s应该变成0 ,null bool? 应该是false ,等等。

NullValueHandling没有帮助,因为我不想Ignore空值,但是我也不想Include它们(嗯,新特性?)。

所以我转向实现一个自定义的JsonConverter
虽然实现本身是一件轻而易举的事,但不幸的是,这仍然没有工作 – CanConvert()永远不会被调用具有null值的属性,因此也不会调用WriteJson() 。 显然,空值会自动直接序列化为null ,而不需要自定义pipe道。

例如,以下是空string的自定义转换器示例:

 public class StringConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(string).IsAssignableFrom(objectType); } ... public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { string strValue = value as string; if (strValue == null) { writer.WriteValue(String.Empty); } else { writer.WriteValue(strValue); } } } 

在debugging器中逐步完成这个工作,我注意到这两个方法都没有为具有空值的属性调用。

深入到JSON.NET的源代码,我发现(显然,我没有深入了很多)有一个特殊情况检查空值,并.WriteNull()地调用.WriteNull()

对于它的价值,我尝试实现一个自定义的JsonTextWriter并覆盖默认的.WriteNull()实现…

 public class NullJsonWriter : JsonTextWriter { ... public override void WriteNull() { this.WriteValue(String.Empty); } } 

但是,这不能正常工作,因为WriteNull()方法对底层数据types一无所知。 所以确定,我可以输出""任何null,但不适用于如int,布尔等。

所以,我的问题 – 手动转换整个数据结构的短,有没有解决方法或解决方法呢?

好吧,我想我已经提出了一个解决scheme(我的第一个解决scheme根本不对,但是我又上了火车)。 您需要为Nullabletypes创build一个特殊的合约parsing器和一个自定义的ValueProvider。 考虑一下:

 public class NullableValueProvider : IValueProvider { private readonly object _defaultValue; private readonly IValueProvider _underlyingValueProvider; public NullableValueProvider(MemberInfo memberInfo, Type underlyingType) { _underlyingValueProvider = new DynamicValueProvider(memberInfo); _defaultValue = Activator.CreateInstance(underlyingType); } public void SetValue(object target, object value) { _underlyingValueProvider.SetValue(target, value); } public object GetValue(object target) { return _underlyingValueProvider.GetValue(target) ?? _defaultValue; } } public class SpecialContractResolver : DefaultContractResolver { protected override IValueProvider CreateMemberValueProvider(MemberInfo member) { if(member.MemberType == MemberTypes.Property) { var pi = (PropertyInfo) member; if (pi.PropertyType.IsGenericType && pi.PropertyType.GetGenericTypeDefinition() == typeof (Nullable<>)) { return new NullableValueProvider(member, pi.PropertyType.GetGenericArguments().First()); } } else if(member.MemberType == MemberTypes.Field) { var fi = (FieldInfo) member; if(fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>)) return new NullableValueProvider(member, fi.FieldType.GetGenericArguments().First()); } return base.CreateMemberValueProvider(member); } } 

然后我用下面的方法testing

 class Foo { public int? Int { get; set; } public bool? Boolean { get; set; } public int? IntField; } 

以下情况:

 [TestFixture] public class Tests { [Test] public void Test() { var foo = new Foo(); var settings = new JsonSerializerSettings { ContractResolver = new SpecialContractResolver() }; Assert.AreEqual( JsonConvert.SerializeObject(foo, Formatting.None, settings), "{\"IntField\":0,\"Int\":0,\"Boolean\":false}"); } } 

希望这有助于一点…

编辑 – 更好地识别Nullable<>types

编辑 – 增加了对字段以及属性的支持,也支持在正常的DynamicValueProvider上进行大部分的工作,使用更新的testing