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.