Reputation: 942
I have used net core 3.1 in my Web API project. To accept the dates in dd-MM-yyyy format from the user in the JSON request body, I have created a DateTime converter, which converts all the incoming as well as outgoing dates to the specified format.
Below is my DateTime Converter:
public class DateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("dd-MM-yyyy hh:mm tt"));
}
}
And I have registered the same in startup.cs like :
services.AddControllers(options =>
{
//To accept browser headers.
options.RespectBrowserAcceptHeader = true;
}).
AddNewtonsoftJson(options =>
{
// Use the default property (Pascal) casing
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
}).
AddJsonOptions(options =>
{
//converter to accept date in particular(dd-MM-yyyy) format
options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
});
Now I am facing certain issues in DateTime Converter. I have created an action filter for checking ModelState Errors which gets invoked after an exception is thrown from DateTime converter. Below is the ModelState Error filter:
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
ApiResponseModel<IEnumerable<ValidationErrorModel>> apiResponseModel = new ApiResponseModel<IEnumerable<ValidationErrorModel>>();
apiResponseModel.ResponseCode = (int)HttpStatusCode.BadRequest;
apiResponseModel.ResponseMessage = string.Join(" ",
context.ModelState.Values.Where(E => E.Errors.Count > 0)
.SelectMany(E => E.Errors)
.Select(E => E.ErrorMessage)
.ToArray());
apiResponseModel.ResponseData = null;
context.Result = new OkObjectResult(apiResponseModel);
}
}
When the date is not in proper format e.g. in MM-dd-YYYY format, it generates ModelState Error and gives its built-in error message as "The supplied value is invalid". I wanted to display a different message instead of this built-in message, something like "The {nameofdatetimefield} is not in a proper format.". So how can I achieve the same inside this DateTimeConverter.?
Upvotes: 1
Views: 2822
Reputation: 41
To address Sunny's issue to ovveride "The supplied value is invalid" message, you have to use throw JsonException instead of FormatException.
public override DateTime Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (DateTime.TryParseExact(
reader.GetString(),
"dd-MM-yyyy hh:mm tt",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime date))
{
return date;
}
throw new JsonException("Property is not in a proper date format.");
}
Upvotes: 4
Reputation: 2627
Within your converter, instead of using an API like DateTime.Parse
that throws an exception for input that is in invalid format, you could use the TryParse
OR TryParseExact
method and throw the exception yourself with a different error message. I am assuming you still want to keep the type of the exception to be the same (i.e. FormatException
).
public override DateTime Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (DateTime.TryParseExact(
reader.GetString(),
"dd-MM-yyyy hh:mm tt",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime date))
{
return date;
}
throw new FormatException("A DateTime property is not in a proper format.");
}
If you are looking to access the property name itself within a converter (to put in an exception message or for any other reason), that is not easily doable. If you really need to, one way to achieve that would be to create a converter for the parent type you are serializing that has the DateTime
property and access it that way. This approach isn't ideal, especially if you happen to have such DateTime
properties in many different parent types (as it would require you to create custom converters for all of them). Regardless, here's a snippet as an example using the typical WeatherForecast
parent type containing a DateTime
that comes with a dotnet new webapi
template:
// The "Read" side of a JsonConverter<WeatherForecast> implementation
public override WeatherForecast Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var forecast = new WeatherForecast();
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
{
if (reader.ValueTextEquals(nameof(forecast.Date)))
{
if (!reader.Read())
{
throw new JsonException();
}
if (DateTime.TryParseExact(
reader.GetString(),
"dd-MM-yyyy hh:mm tt",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime date))
{
forecast.Date = date;
}
else
{
throw new FormatException(
$"The {nameof(forecast.Date)} is not in a proper format.");
}
}
// Add logic for creating the rest of the object properties from the JSON.
}
return forecast;
}
Upvotes: 0