I have an ASP.NET Web API that accepts json requests. The default JsonMediaTypeFormatter is used.
configuration.Formatters.Add(new JsonMediaTypeFormatter());
On the model I use a custom ValidationAttribute to ensure a string value provided matches the expected date time format. Example of a valid value = "2020-04-21T11:30:43+03:00"
Code snippet from my model (which obviously contains many more properties):
[Required]
[DateFormat("yyyy-MM-ddTHH:mm:sszzz")]
public string RegistrationTimestamp { get; set; }
public string RegisteredValue { get; set; }
Code snippet from the DateFormat ValidationAttribute shown below:
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (value == null)
{
return ValidationResult.Success;
}
if (DateTime.TryParseExact((string)value, _dateFormat, new CultureInfo("nl-NL"), DateTimeStyles.AdjustToUniversal, out DateTime date))
{
return ValidationResult.Success;
}
return new ValidationResult($"Date provided [{AntiXssEncoder.HtmlEncode(value.ToString(), true)}] is not formatted as [{_dateFormat}].");
}
The above works fine.
I noticed that when an empty string value is provided as RegisteredValue it ends up in the model as an empty string, while I would like to have them in the model as null instead. That's why I decided to introduce a custom JsonConverter to convert empty strings to null.
The configuration is adjusted:
configuration.Formatters.Add(new JsonMediaTypeFormatter());
configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new EmptyStringToNullConverter());
The custom JsonConverter is implemented:
public class EmptyStringToNullConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.Value == null)
{
return null;
}
var text = reader.Value.ToString();
if (string.IsNullOrWhiteSpace(text))
{
return null;
}
return text;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException("Not needed because this converter cannot write json");
}
public override bool CanWrite
{
get { return false; }
}
}
The logic of the JsonConverter works fine and it ensures an empty string is converted to null. Unfortunately adding this JsonConverter seems to affect how string values are interpreted, because the value passed to the DateFormat ValidationAttribute is no longer the string value as provided to the API but it has changed from "2020-04-21T11:30:43+03:00" into "21-Apr-20 10:30:43".
How can adding a custom JsonConverter affect the value that is being passed to the ValidationAttribute when this custom JsonConvertor does nothing regarding date parsing?
Apparently I am not the first one to encounter this issue, as I found out when browsing the GitHub issues on the Newtonsoft.Json package: https://github.com/JamesNK/Newtonsoft.Json/issues/904
So eventually I ended up setting DateParseHandling to None while keeping my custom JsonConvertor as well.
configuration.Formatters.Add(new JsonMediaTypeFormatter());
configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new EmptyStringToNullConverter());
configuration.Formatters.JsonFormatter.SerializerSettings.DateParseHandling = DateParseHandling.None;