簡體   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