Peter Morris
Peter Morris

Reputation: 23224

Why does ASPMVCCore pass null to my JsonConverter?

Here is my JSON request object

public class CategoryCreateOrUpdateCommand: IRequest<BaseApiStateResponse>
{
    public Guid? Id { get; set; }
    public string Name { get; set; }
}

My controller action looks like this

    [HttpPost, Route("api/category-put")]
    public Task<BaseApiStateResponse> CreateOrUpdate([FromBody] CategoryCreateOrUpdateCommand command)
    {
        //Do stuff
    }

I have the following JsonConverter which converts between a Guid and Base64 encoded string in order to lower traffic.

public class ShortGuidConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Guid) || objectType == typeof(Guid?);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (existingValue == null)
            return null;

        string base64Value = existingValue + "==";
        byte[] bytes = Convert.FromBase64String(base64Value);
        return new Guid(bytes);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Guid? guid = (Guid?)value;
        if (!guid.HasValue)
        {
            writer.WriteNull();
        } 
        else
        {
            string base64Value = Convert.ToBase64String(guid.Value.ToByteArray());
            base64Value = base64Value.Substring(0, base64Value.Length - 2);
            writer.WriteValue(base64Value);
        }
    }
}

This serializes outgoing Guids nicely, but when receiving a request from a client the existingValue parameter passed into ReadJson is always null.

What have I missed? The registration is as follows:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMediatR();
        services.AddBusinessDomain(Configuration);
        RegisterWebServerDependencies(services);
        AddCors(services);
        services.AddMvcCore(config =>
        {
            config.Filters.Add(new UnhandledExceptionFilter());
            config.Filters.Add(new ModelValidationFilter());
            config.Filters.Add(typeof(AuthenticationFilter));
            config.Filters.Add(typeof(DetectPreferredThemeFilter));
            config.Filters.Add(typeof(DetectReferrerUrlFilter));
            config.ModelBinderProviders.Add(new ValueProviders.ShortGuidBindingProvider());
        })
        .AddAuthorization()
        .AddDataAnnotations()
        .AddJsonFormatters(opts =>
        {
            opts.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
            opts.Converters.Add(new ShortGuidConverter());
        });
        services.AddAutoMapper();
    }

Upvotes: 0

Views: 37

Answers (1)

Peter Morris
Peter Morris

Reputation: 23224

The solution is to read from reader.Value instead.

Upvotes: 1

Related Questions