Newtonsoft.Json icon indicating copy to clipboard operation
Newtonsoft.Json copied to clipboard

When using both `DefaultValueHandling.Ignore` and `NullValueHandling.Ignore`, the nullable<int> field is serialized when value is default(int)

Open jessehouwing opened this issue 5 years ago • 2 comments

When using both DefaultValueHandling.Ignore and NullValueHandling.Ignore, the field is serialized

Source/destination types

public class Test
{
    public int? IgnoresNull = null;
    public int? IgnoreZeroPlease = 0;
}

Source/destination JSON

{"IgnoreZeroPlease": 0}

Expected behavior

{}

Ignores both null as well as default(int).

Actual behavior

Only ignores null

Is the best solution to fix this to provide a ValueConvertor that returns Null when the default is specified?

jessehouwing avatar Jul 10 '20 12:07 jessehouwing

This seems to work, but it feels ugly:

        protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
        {
            IValueProvider parent = base.CreateMemberValueProvider(member);

            var type = (member as FieldInfo)?.FieldType
                ?? (member as PropertyInfo)?.PropertyType;

            if (type == typeof(decimal?))
            {
                return new DefaultToNullValueProvider<decimal>(parent);
            }

    ...

With:

internal class DefaultToNullValueProvider<T> : IValueProvider
    where T : struct
{
    private IValueProvider _parent;

    internal DefaultToNullValueProvider(IValueProvider parent)
    {
        _parent = parent;
    }

    public object GetValue(object target)
    {
        object o = _parent.GetValue(target);

        var value = (T?)o;
        if (value != null && value.HasValue && value.Value.Equals(default(T)))
        {
            return null;
        }

        return o;
    }

    public void SetValue(object target, object value)
    {
        _parent.SetValue(target, value);
    }
}

jessehouwing avatar Jul 10 '20 14:07 jessehouwing

When using both DefaultValueHandling.Ignore and NullValueHandling.Ignore, the field is serialized

Source/destination types

public class Test
{
    public int? IgnoresNull = null;
    public int? IgnoreZeroPlease = 0;
}

Source/destination JSON

{"IgnoreZeroPlease": 0}

Expected behavior

{}

Ignores both null as well as default(int).

Actual behavior

Only ignores null

Is the best solution to fix this to provide a ValueConvertor that returns Null when the default is specified?

This has been logged a long time ago but the behavior is correct when you look at it from a deserialization point of view. If you try deserialize "{}" there would be no way to distinguish between a null or default property. Nullable properties can thus inherently never apply the default property and null rule simultaneously.

ShaunHavelaar91 avatar Oct 17 '23 05:10 ShaunHavelaar91