繁体   English   中英

在JSON.NET中序列化null

[英]Serializing null in JSON.NET

通过JSON.NET序列化任意数据时,任何为null的属性都将按以下方式写入JSON:

“ propertyName”:null

当然,这是正确的。

但是我有一个要求自动将所有null转换为默认的空值,例如null string应该变成String.Empty ,null int? s应该变为0bool?值无效bool? s应该为false ,依此类推。

NullValueHandling没有帮助,因为我不想Ignore null,但是我也不想Include它们(嗯,新功能?)。

因此,我转向实现自定义JsonConverter
虽然实现本身很CanConvert() ,但是不幸的是,这种方法仍然没有用-从来没有为具有null值的属性调用WriteJson() ,因此也没有调用WriteJson() 显然,没有自定义管道,空值会自动直接直接序列化为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);
        }
    }
}

在调试器中逐步进行此操作时,我注意到,对于具有空值的属性,都不会调用这两种方法。

深入研究JSON.NET的源代码,我发现(显然,我没有深入探讨)有一种特殊情况,它检查null,并显式调用.WriteNull()

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

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

但是,这不能很好地工作,因为WriteNull()方法对基础数据类型一无所知。 因此,可以肯定,我可以为任何null输出"" ,但是对于int,bool等来说效果不佳。

因此,我的问题-除了手动转换整个数据结构外,是否有解决方案或解决方法?

好的,我想我想出了一个解决方案(我的第一个解决方案根本不对,但随后我又在火车上)。 您需要为Nullable类型创建一个特殊的合同解析器和一个自定义的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上附带了更新的测试,以完成大部分工作

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM