Home > Back-end >  Newtonsoft Json - Different Null Handling values for Serialization and Deserialization
Newtonsoft Json - Different Null Handling values for Serialization and Deserialization

Time:11-01

I want to enable the null request values while my API deserializes http request body but I do not want my api to ignore null values while sending the response. I.e.:

  • Ignore null values when deserializing
  • Do not ignore null values when serializing

How can I achieve this globally?

This was not an issue on .Net Framework 4.7.2, and our front-end was designed to meet above criteria. Now it's creating this huge problem while we convert our api to .NET 5.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Serialization;

public void ConfigureServices(IServiceCollection services)
{

    services.AddNewtonsoftJson(options =>
             {
                 options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Include; // This enables null values for both serialization/deserialization which does not suit my needs.
     
                 (options.SerializerSettings.ContractResolver as DefaultContractResolver).NamingStrategy = null;
             })
             .AddJsonOptions(jsonOptions =>
             {
                 // Create output in PascalCase instead of camelCase
                 jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null;
                 jsonOptions.JsonSerializerOptions.NumberHandling = System.Text.Json.Serialization.JsonNumberHandling.AllowReadingFromString;
                 jsonOptions.JsonSerializerOptions.DefaultIgnoreCondition =          System.Text.Json.Serialization.JsonIgnoreCondition.Never;
             })
}

CodePudding user response:

If you need a "pure" Json.NET setting that

  • Skips null values when deserializing.
  • Emits null values when serializing.

Then I am unaware of any built-in serializer setting for this specifically. Instead what you could do is add a root-level converter that sets serializer.NullValueHandling = NullValueHandling.Ignore when deserializing, and does nothing when serializing:

public class IgnoreNullValuesWhenDeserializingRootConverter : JsonConverter
{
    // Disables the converter in a thread-safe manner to prevent a stack overflow as the converter calls itself.
    [ThreadStatic]
    static bool disabled;
    bool Disabled { get { return disabled; } set { disabled = value; } }

    public override bool CanWrite => false;
    public override bool CanRead => !Disabled;
    public override bool CanConvert(Type objectType) => !Disabled;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    {
        using (new PushValue<bool>(true, () => Disabled, val => Disabled = val))
        using (new PushValue<NullValueHandling>(NullValueHandling.Ignore, () => serializer.NullValueHandling, val => serializer.NullValueHandling = val))
        {
            return serializer.Deserialize(reader, objectType);
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}

public struct PushValue<T> : IDisposable
{
    Action<T> setValue;
    T oldValue;

    public PushValue(T value, Func<T> getValue, Action<T> setValue)
    {
        if (getValue == null || setValue == null)
            throw new ArgumentNullException();
        this.setValue = setValue;
        this.oldValue = getValue();
        setValue(value);
    }

    #region IDisposable Members

    // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
    public void Dispose() => setValue?.Invoke(oldValue);

    #endregion
}

Then initialize options as follows:

options.SerializerSettings.Converters.Add(new IgnoreNullValuesWhenDeserializingRootConverter()); //Ignore null values when deserializing
options.SerializerSettings.NullValueHandling = NullValueHandling.Include; // Do not ignore null values when serializing

Notes:

  • This will not work if your root model also has its own custom JsonConverter applied.

Demo fiddle here.

  • Related