System.Text.Json.JsonException:non è stato possibile convertire il valore JSON in System.DateTime

System.Text.Json.JsonException:non è stato possibile convertire il valore JSON in System.DateTime

Quando si deserializza un valore datetime con System.Text.Json.JsonSerializer, se il valore non è nel formato previsto, genererà un'eccezione JsonException. Prevede che datetime siano nel formato ISO-8601-1:2019 (ad esempio:2021-07-12T12:35:34+00:00).

Ad esempio, il codice seguente tenta di deserializzare un valore datetime in un formato imprevisto:

var eventJson = "{\"HappenedAt\":\"2021-05-26 18:30:41.720\", \"Name\":\"Meltdown\"}";
var sysEvent = JsonSerializer.Deserialize<SystemEvent>(eventJson);
Code language: C# (cs)

Questo genererà la seguente eccezione:

Supponendo che tu debba deserializzare il JSON con il formato datetime così com'è (e non puoi cambiarlo), la soluzione è creare e utilizzare un convertitore personalizzato. Mostrerò come farlo di seguito.

Nota:questo problema si verifica per DateTime, DateTime?, DateTimeOffset e DateTimeOffset? e la soluzione è quasi la stessa per tutti questi casi.

Soluzione:utilizza un convertitore di data e ora personalizzato

I passaggi seguenti mostrano come creare un convertitore personalizzato che deserializza un valore datetime con qualsiasi formato tu stia utilizzando.

Fase 1 – Sottoclasse JsonConverter

Per creare un convertitore datetime personalizzato, sottoclasse JsonConverter in questo modo:

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)

Fase 2 – Implementa Read()

Dal momento che vuoi personalizzare la deserializzazione, dovrai implementare Read(). Non è necessario implementare Write() (a meno che non utilizziate anche il convertitore personalizzato per la serializzazione).

Ad esempio, puoi utilizzare DateTime.ParseExact() se vuoi solo essere in grado di analizzare il tuo formato esatto:

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() funzionerebbe anche su datetime (2021-05-26 18:30:41.720) utilizzato in questo articolo:

public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
	return DateTime.Parse(reader.GetString());
}
Code language: C# (cs)

Fai attenzione con l'utilizzo di DateTime.Parse() poiché può analizzare un'ampia gamma di formati. Se vuoi accettare solo un formato molto specifico, usa invece ParseExact().

Fase 3:passa il convertitore personalizzato a JsonSerializer

Per utilizzare il convertitore personalizzato, aggiungilo a JsonSerializerOptions.Converters, quindi passa le opzioni a JsonSerializer.Deserialize(), in questo modo:

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)

Quando JsonSerializer viene eseguito nella proprietà HappenedAt, chiamerà il convertitore personalizzato, che analizzerà correttamente il valore datetime.