在 JSON.NET 中序列化 null [英] Serializing null in JSON.NET

查看:34
本文介绍了在 JSON.NET 中序列化 null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当通过 JSON.NET 序列化任意数据时,任何为 null 的属性都会作为

When serializing arbitrary data via JSON.NET, any property that is null is written to the JSON as

"propertyName" : null

"propertyName" : null

这当然是正确的.

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

However I have a requirement to automatically translate all nulls into the default empty value, e.g. null strings should become String.Empty, null int?s should become 0, null bool?s should be false, and so on.

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

NullValueHandling is not helpful, since I dont want to Ignore nulls, but neither do I want to Include them (Hmm, new feature?).

所以我转向实现自定义JsonConverter.
虽然实现本身轻而易举,但不幸的是这仍然不起作用 - CanConvert() 永远不会为具有空值的属性调用,因此 WriteJson() 不是叫要么.显然,空值会自动直接序列化为 null,无需自定义管道.

So I turned to implementing a custom JsonConverter.
While the implementation itself was a breeze, unfortunately this still didnt work - CanConvert() is never called for a property that has a null value, and therefore WriteJson() is not called either. Apparently nulls are automatically serialized directly into null, without the custom pipeline.

例如,以下是用于空字符串的自定义转换器示例:

For example, here is a sample of a custom converter for null strings:

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);
        }
    }
}

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

Stepping through this in the debugger, I noted that neither of these methods are called for properties that have a null value.

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

Delving into JSON.NET's sourcecode, I found that (apparently, I didnt go into a lot of depth) there is a special case checking for nulls, and explictly calling .WriteNull().

就其价值而言,我确实尝试实现自定义 JsonTextWriter 并覆盖默认的 .WriteNull() 实现...

For what it's worth, I did try implementing a custom JsonTextWriter and overriding the default .WriteNull() implementation...

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

然而,这不能很好地工作,因为 WriteNull() 方法对底层数据类型一无所知.所以当然,我可以为任何空值输出 "" ,但这不适用于例如int、bool 等.

However, this can't work well, since the WriteNull() method knows nothing about the underlying datatype. So sure, I can output "" for any null, but that doesnt work well for e.g. int, bool, etc.

所以,我的问题是 - 没有手动转换整个数据结构,是否有任何解决方案或解决方法?

So, my question - short of converting the entire data structure manually, is there any solution or workaround for this?

推荐答案

好吧,我想我想出了一个解决方案(我的第一个解决方案根本不正确,但后来我又在火车上).您需要为 Nullable 类型创建一个特殊的合约解析器和一个自定义的 ValueProvider.考虑一下:

Okay, I think I've come up with a solution (my first solution wasn't right at all, but then again I was on the train). You need to create a special contract resolver and a custom ValueProvider for Nullable types. Consider this:

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}");
    }
}

希望这会有所帮助...

Hopefully this helps a bit...

编辑更好地识别 Nullable<> 类型

编辑添加了对字段和属性的支持,还捎带在普通 DynamicValueProvider 之上来完成大部分工作,并更新测试

Edit – Added support for fields as well as properties, also piggy-backing on top of the normal DynamicValueProvider to do most of the work, with updated test

这篇关于在 JSON.NET 中序列化 null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆