在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