在 JSON.NET 中序列化空值
本文关键字:序列化 空值 NET JSON | 更新日期: 2023-09-27 18:20:01
通过 JSON.NET 序列化任意数据时,任何为 null 的属性都将作为 JSON 写入 JSON
"属性名称" : 空
当然,这是正确的。
但是,我需要将所有 null 自动转换为默认的空值,例如 null string
s 应该变成 String.Empty
,null int?
s 应该变成 0
,null bool?
s 应该false
,等等。
NullValueHandling
没有帮助,因为我不想Ignore
空值,但我也不想Include
它们(嗯,新功能?
所以我转向实现自定义JsonConverter
。
虽然实现本身轻而易举,但不幸的是,这仍然不起作用 - CanConvert()
永远不会为具有 null 值的属性调用,因此也不会调用WriteJson()
。显然,null 会自动直接序列化到 null
中,而无需自定义管道。
例如,下面是空字符串的自定义转换器的示例:
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);
}
}
}
在调试器中逐步完成此操作时,我注意到这些方法都没有为具有 null 值的属性调用。
深入研究 JSON。NET的源代码,我发现(显然,我没有深入(有一个特殊情况检查null,并显式调用.WriteNull()
。
对于它的价值,我确实尝试实现自定义JsonTextWriter
并覆盖默认.WriteNull()
实现......
public class NullJsonWriter : JsonTextWriter
{
...
public override void WriteNull()
{
this.WriteValue(String.Empty);
}
}
但是,这不能很好地工作,因为WriteNull()
方法对基础数据类型一无所知。当然,我可以为任何 null 输出""
,但这不适用于例如 int、bool 等。
所以,我的问题 - 除了手动转换整个数据结构之外,是否有任何解决方案或解决方法?
好的,我想我已经想出了一个解决方案(我的第一个解决方案根本不对,但后来我又在火车上(。您需要为可为 Null 的类型创建特殊的协定解析程序和自定义 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);
}
}
然后我使用以下方法对其进行了测试:
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<>
类型
编辑 – 添加了对字段和属性的支持,还搭载在正常DynamicValueProvider
之上以完成大部分工作,并更新了测试