Search code examples
c#asp.net-coreasp.net-core-5.0

System.Text.Json.Serialization.JsonConverter<DateTime> is executed for nullable DateTime properties as well


So, basically I wanted to create a custom converter in order to convert datetime values according to user timezone. Here is the code:

public class UserBasedDateTimeJsonConverter : System.Text.Json.Serialization.JsonConverter<DateTime>
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public UserBasedDateTimeJsonConverter(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override bool CanConvert(Type typeToConvert)
    {
        return typeof(DateTime) == typeToConvert;
    }

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TryGetDateTime(out var date))
        {
            return date;
        }

        throw new Exception("Error");
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        if (_httpContextAccessor.HttpContext is not null)
        {
            var securityContextProvider = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ISecurityContextProvider>();

            var securityContext = securityContextProvider.SecurityContext;

            if (securityContext.IsAuthenticated)
            {
                try
                {
                    writer.WriteStringValue(TimeZoneInfo.ConvertTimeFromUtc(value, securityContext.UserToken.TimeZoneInfo));
                    return;
                }
                catch (Exception e)
                {
                    var logger = _httpContextAccessor.HttpContext.RequestServices.GetRequiredService<ILogger<UserBasedDateTimeJsonConverter>>();
                    logger.LogInformation(e, e.Message);
                }
            }
        }

        writer.WriteStringValue(value);
    }
}

public class CustomJsonOptions : IConfigureOptions<JsonOptions>
{
    readonly IHttpContextAccessor _accessor;

    public FeatureConfigJsonOptions(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public void Configure(JsonOptions options)
    {
        options.JsonSerializerOptions.Converters.Add(new UserBasedDateTimeJsonConverter(_accessor));
    }
}

It works fine but the only odd thing is that when I have a custom model which contains a nullable DateTime value, the converter -> read method will hit as well. And, of course for nullable types I dont know what to return.

Why would this converter try to read the data if the property is nullable DateTime ?

EDIT:

Add some more info:

Basically my request model is:

public class NewApplicationKeyRequest
{
    public Guid ApplicationId { get; set; }
    public string Description { get; set; } = string.Empty;
    public DateTime? Expired { get; set; }
}

But when I do the request the CanConvert is hit 5 times:

  1. For NewApplicationKeyRequest
  2. For Guid -> ApplicationId
  3. For string -> Description
  4. For DateTime? -> Expired
  5. For DateTime -> No idea why this is somehow created under the hood by Asp.Net core. Maybe is some sort of backing field for the Expired. And this is the one causing the problem.

Solution

  • After adding also a System.Text.Json.Serialization.JsonConverter<DateTime?> class it seems that the non-nullable version was not hit anymore.