Search code examples
c#.netasp.net-corejson.netasp.net-core-2.2

ASP.Net Core 2.2 - separate serializer settings for input and output


ASP.Net Core 2.2 allows to set serializer settings using MvcJsonOptions.SerializerSettings property. The problem is that it affects both input and output. Is there a way to have separate options for input (deserialization) and output (serialization)? In particular, I need to set a different behavior for NullValueHandling settings: ignore null errors for non-nullable fields when deserializing client json but keep nulls for defined model fields when serializing the result.

For example, I have a C# model for request:

public class SomeEntity
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Name { get; set; }
}

And input JSON: { id: null, parentId: null, name: "test" }. The deserialization fails for NullValueHandling.Include but works for NullValueHandling.Ignore.

But when I serialize an entity like this one

new SomeEntity
{
    Id = 1,
    ParentId = null,
    Name = "test"
}

It keeps null with NullValueHandling.Include: { id: 1, parentId: null, name: "test" } but erases it with NullValueHandling.Ignore: { id: 1, name: "test" }.

I need to achieve the "Ignore" scenario for input and "Include" for output.


Solution

  • Finally found this workaround: https://github.com/aspnet/Mvc/issues/4562#issuecomment-226100352

    public class CustomSerializerSettingsSetup : IConfigureOptions<MvcOptions>
    {
        private readonly ILoggerFactory _loggerFactory;
        private readonly ArrayPool<char> _charPool;
        private readonly ObjectPoolProvider _objectPoolProvider;
    
        public CustomSerializerSettingsSetup(
            ILoggerFactory loggerFactory,
            ArrayPool<char> charPool,
            ObjectPoolProvider objectPoolProvider)
        {
            _loggerFactory = loggerFactory;
            _charPool = charPool;
            _objectPoolProvider = objectPoolProvider;
        }
    
        public void Configure(MvcOptions options)
        {
            options.OutputFormatters.RemoveType<JsonOutputFormatter>();
            options.InputFormatters.RemoveType<JsonInputFormatter>();
            options.InputFormatters.RemoveType<JsonPatchInputFormatter>();
    
            var outputSettings = new JsonSerializerSettings();
            options.OutputFormatters.Add(new JsonOutputFormatter(outputSettings, _charPool));
    
            var inputSettings = new JsonSerializerSettings();
            var jsonInputLogger = _loggerFactory.CreateLogger<JsonInputFormatter>();
            options.InputFormatters.Add(new JsonInputFormatter(
                jsonInputLogger,
                inputSettings,
                _charPool,
                _objectPoolProvider));
    
            var jsonInputPatchLogger = _loggerFactory.CreateLogger<JsonPatchInputFormatter>();
            options.InputFormatters.Add(new JsonPatchInputFormatter(
                jsonInputPatchLogger,
                inputSettings,
                _charPool,
                _objectPoolProvider));
        }
    }
    

    and

    services.TryAddEnumerable(
        ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, CustomSerializerSettingsSetup>());
    

    in service provider configuration