C# 在 JSON.NET 中序列化 null

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8833961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 04:42:58  来源:igfitidea点击:

Serializing null in JSON.NET

c#.netserializationjson.net

提问by AviD

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

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

"propertyName" : null

“财产名称”:空

This is correct, of course.

这当然是正确的。

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.

但是我需要自动将所有空值转换为默认的空值,例如 null strings should become String.Empty、 null int?s should become 0、 null bool?s should be false,等等。

NullValueHandlingis not helpful, since I dont want to Ignorenulls, but neither do I want to Includethem (Hmm, new feature?).

NullValueHandling没有帮助,因为我不想Ignore归零,但我也不想要Include他们(嗯,新功能?)。

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.

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

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.

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

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().

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

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

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

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

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.

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

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

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

采纳答案by J. Holmes

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:

好的,我想我已经想出了一个解决方案(我的第一个解决方案根本不正确,但后来我又在火车上)。您需要为 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);
    }
}

Then I tested it using:

然后我使用以下方法对其进行了测试:

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

And the following case:

以及以下情况:

[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...

希望这会有所帮助...

Edit – Better identification of the a Nullable<>type

编辑 - 更好地识别 aNullable<>类型

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

编辑 - 添加了对字段和属性的支持,并在正常工作的基础DynamicValueProvider上进行了大部分工作,并更新了测试