When you’re using System.Text.Json to deserialize a DateTime value, if the value isn’t in the format it expects, then it’ll throw a JsonException. It expects datetimes to be in the ISO-8601-1:2019 format (for example: 2021-07-12T12:35:34+00:00).
For example, the following code is trying to deserialize a DateTime value in an unexpected format:
var eventJson = "{\"HappenedAt\":\"2021-05-26 18:30:41.720\", \"Name\":\"Meltdown\"}";
var sysEvent = JsonSerializer.Deserialize<SystemEvent>(eventJson);
Code language: C# (cs)
This’ll throw the following exception:
System.Text.Json.JsonException: The JSON value could not be converted to System.DateTime. Path: $.HappenedAt | LineNumber: 0 | BytePositionInLine: 39.
—> System.FormatException: The JSON value is not in a supported DateTime format.
Assuming you must deserialize the JSON with the DateTime format as is (and can’t change it), then the solution is to create and use a custom JSON converter. I’ll show how to do that below.
Note: This problem happens for DateTime, DateTime?, DateTimeOffset, and DateTimeOffset?, and the solution is nearly the same for all these cases.
Solution – Use a custom JSON converter to handle DateTime
The following steps show how to create a custom converter that deserializes a DateTime value with whatever format you’re using.
Step 1 – Subclass JsonConverter
To create a custom DateTime converter, subclass JsonConverter like this:
using System.Text.Json;
using System.Text.Json.Serialization;
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
//Don't implement this unless you're going to use the custom converter for serialization too
throw new NotImplementedException();
}
}
Code language: C# (cs)
Step 2 – Implement Read()
Since you want to customize deserialization, you’ll need to implement Read(). You don’t need to implement Write() (unless you’re also going to use the custom converter for serialization).
In Read(), use reader.GetString() and then parse the DateTime from the string. For example, you can use DateTime.ParseExact() if you only want to be able to parse your exact format:
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.ParseExact(reader.GetString(), "yyyy-MM-dd H:mm:ss.fff", null);
}
Code language: C# (cs)
DateTime.Parse() would also work on the datetime (2021-05-26 18:30:41.720) used in this article:
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
Code language: C# (cs)
Be careful with using DateTime.Parse() since it can parse a wide range of formats. If you want to only accept a very specific format, use ParseExact() instead.
Step 3 – Pass in the custom converter to JsonSerializer
To use the custom converter, add it to JsonSerializerOptions.Converters, and then pass the options into JsonSerializer.Deserialize(), like this:
var eventJson = "{\"HappenedAt\":\"2021-05-26 18:30:41.720\", \"Name\":\"Meltdown\"}";
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomDateTimeConverter());
var sysEvent = JsonSerializer.Deserialize<SystemEvent>(eventJson, options);
Code language: C# (cs)
When JsonSerializer runs into the HappenedAt property, it’ll call the custom converter, which will successfully parse the datetime value.