Deserializing dates with dd/MM/yyyy format using Json.Net
I'm trying to deserialize an object from JSON data to a C# class (I'm using Newtonsoft Json.NET). The data contains dates as string values like 09/12/2013
where the format is dd/MM/yyyy
.
If I call JsonConvert.DeserializeObject<MyObject>(data)
,
dates are loaded to the DateTime
property of the C# class with the MM/dd/yyyy
format, this causes the date value to be 12 September 2013
(instead of 9 December 2013
).
Is it possible to configure JsonConvert
to get the date in the correct format?
You can use an IsoDateTimeConverter
and specify the DateTimeFormat
to get the result you want, e.g.:
MyObject obj = JsonConvert.DeserializeObject<MyObject>(jsonString,
new IsoDateTimeConverter { DateTimeFormat = "dd/MM/yyyy" });
Demo:
class Program
{
static void Main(string[] args)
{
string json = @"{ ""Date"" : ""09/12/2013"" }";
MyObject obj = JsonConvert.DeserializeObject<MyObject>(json,
new IsoDateTimeConverter { DateTimeFormat = "dd/MM/yyyy" });
DateTime date = obj.Date;
Console.WriteLine("day = " + date.Day);
Console.WriteLine("month = " + date.Month);
Console.WriteLine("year = " + date.Year);
}
}
class MyObject
{
public DateTime Date { get; set; }
}
Output:
day = 9
month = 12
year = 2013
The starting sections are about NewtownSoft converter, and the reset is about .Net Core Json Serializer, since there was no Microsoft serializer when i first wrote this answer
Note: NewtownSoft and Microsoft have so many overlapping names, make sure you use the right namespaces
Newtownsoft Serializer:
Multi Format Support:
This is what i use:
public class CustomDateTimeConverter : IsoDateTimeConverter
{
public CustomDateTimeConverter()
{
base.DateTimeFormat = "dd/MM/yyyy";
}
}
then you will do this:
public class MyObject
{
[JsonConverter(typeof(CustomDateTimeConverter))]
public DateTime Date {get;set;}
}
and then deserialize in using any normal way you did before...
MyObject obj = JsonConvert.DeserializeObject<MyObject>(json);
Alternative
Other way would be same to what @pimbrouwers said:
public class MyObject
{
[JsonProperty("Date")] //Naturally Case Sensetive
private string dateJson {get;set;}
// it would be good to look at @pimbrouwers answer and use nullable
[JsonIgnore]
public DateTime Date
{
get
{
return DateTime.ParseExact(dateJson,"dd/MM/yyyy",CultureInfo.InvariantCulture);
}
set
{
dateJson = value.ToString("dd/MM/yyyy");
}
}
}
Custom DateTimeConverter
Also i just write this, Custom DateTime Converter that would match your custom out of scope format or calendar
public class CustomDateTimeConverterJalali : DateTimeConverterBase
{
//I had no use for WriteJson section, i just wrote it, so i do not guarantee it working
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
writer.WriteNull();
return;
}
var nullableType = Nullable.GetUnderlyingType(value.GetType());
var isNullable = nullableType != null;
DateTime date;
if (isNullable)
date = ((DateTime?) value).Value;
else
date = (DateTime) value;
PersianCalendar pc = new PersianCalendar();
writer.WriteValue(pc.GetYear(date) + "/" + pc.GetMonth(date) + "/" + pc.GetDayOfMonth(date));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//this should likely be null, but since the provider json returned empty string, it was unavoidable... (i'm not sure what we will read using reader, if data is actually null on the json side, feel free to experiment
if (string.IsNullOrWhiteSpace((string) reader.Value))
{
return null;
}
var strDate = reader.Value.ToString();
PersianCalendar pc = new PersianCalendar();
var dateParts = strDate.Split('/');
DateTime date = pc.ToDateTime(int.Parse(dateParts[0]), int.Parse(dateParts[1]), int.Parse(dateParts[2]),
0, 0, 0, 0);
return date;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);//DateTime=>true | DateTime?=>true
}
}
Note:
The other ways you provide are one time configured and may, be simpler, and can be useful at most of times,... but here, the provider, provide me with a service, that provide dates in two format in each object, and one of these object provide two date each one in different calendar... so it's good to know this two ways i provide in here
Microsoft Serializer:
[ASP.NET CORE MVC] Custom DateTimeConverter (Via default JSON serializer):
Note that Microsoft implementation of JSON converter is different that NewtownSoft implementation. I hope NewtownSoft flag won't come down soon, as they put all their life on it, but people tend to use library of those in row of power, so here it is.
Note also that, the Microsoft implementation requires you to implement each type of data includes Nullable and non-Nullable separately.
Here is how I did it, I create a base class include all shared stuff, and then create a simple derived one for each version.
The Base:
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Charter724.Helper.JsonConverter.Microsoft
{
/// <summary>
/// Base Custom Format DateTime Handler <br/>
/// using System.Text.Json.Serialization;
/// </summary>
[SuppressMessage("ReSharper", "RedundantBaseQualifier")]
public class MsBaseDateTimeConverter<T> : JsonConverter<T>
{
private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK";
private readonly string _format;
private readonly CultureInfo _culture;
private readonly DateTimeStyles _dateTimeStyles;
public MsBaseDateTimeConverter(string format, CultureInfo culture = null, DateTimeStyles dateTimeStyles= DateTimeStyles.RoundtripKind)
{
_format = format;
if (culture == null)
{
_culture = CultureInfo.CurrentCulture;
}
_dateTimeStyles = dateTimeStyles;
}
public override bool CanConvert(Type typeToConvert)
{
if (typeToConvert == typeof(DateTime) || typeToConvert == typeof(DateTime?))
{
return true;
}
return false;
}
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
bool nullable = ReflectionUtils.IsNullableType(typeToConvert);
if (reader.TokenType == JsonTokenType.Null)
{
if (!nullable)
{
throw new JsonException();
}
return default;
}
if (_format != null)
{
if (DateTime.TryParseExact(reader.GetString(), _format, _culture, _dateTimeStyles,
out var dtValue))
{
return (T) (object) dtValue;
}
throw new JsonException();
}
else
{
// try to parse number directly from bytes
ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
if (Utf8Parser.TryParse(span, out DateTime dtValue, out int bytesConsumed) &&
span.Length == bytesConsumed)
return (T) (object) dtValue;
// try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
if (DateTime.TryParse(reader.GetString(), out dtValue))
return (T) (object) dtValue;
return (T) (object) reader.GetDateTime();
}
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
if (value != null)
{
if (value is DateTime dateTime)
{
if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal
|| (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal)
{
dateTime = dateTime.ToUniversalTime();
}
var text = dateTime.ToString(_format ?? DefaultDateTimeFormat, _culture);
writer.WriteStringValue(text);
}
else
{
throw new JsonException();
}
return;
}
writer.WriteNullValue();
}
}
}
The derived one for non-nullable:
using System;
using System.Diagnostics.CodeAnalysis;
namespace Charter724.Helper.JsonConverter.Microsoft
{
/// <summary>
/// Format: yyyy-MM-dd - NOT NULL <br/>
/// Microsoft <br/>
/// using System.Text.Json.Serialization;
/// </summary>
[SuppressMessage("ReSharper", "RedundantBaseQualifier")]
public class MsCustomDateTimeConverter : MsBaseDateTimeConverter<DateTime>
{
public MsCustomDateTimeConverter():base("yyyy-MM-dd")
{
//base.DateTimeFormat = "yyyy-MM-dd";
}
}
}
The derived one for nullable:
using System;
using System.Diagnostics.CodeAnalysis;
namespace Charter724.Helper.JsonConverter.Microsoft
{
/// <summary>
/// Format: yyyy-MM-dd - NULLABLE <br/>
/// Microsoft <br/>
/// using System.Text.Json.Serialization;
/// </summary>
[SuppressMessage("ReSharper", "RedundantBaseQualifier")]
public class MsCustomDateTimeConverterNullable : MsBaseDateTimeConverter<DateTime?>
{
public MsCustomDateTimeConverterNullable():base("yyyy-MM-dd")
{
//base.DateTimeFormat = "yyyy-MM-dd";
}
}
}
Usage Details:
public class MyObject
{
[System.Text.Json.Serialization.JsonConverter(typeof(MsCustomDateTimeConverter))]
public DateTime Date { set; get; }
}
Alternative:
I didn't test if microsoft version also support private members with JsonProperty or not, but since EF Core failed in this matter, I just wanted to note the matter, in case it didn't worked.
public class MyObject
{
[JsonProperty("Date")] //Naturally Case Sensetive
private string dateJson {get;set;}
// it would be good to look at @pimbrouwers answer and use nullable
[JsonIgnore]
public DateTime Date
{
get
{
return DateTime.ParseExact(dateJson,"dd/MM/yyyy",CultureInfo.InvariantCulture);
}
set
{
dateJson = value.ToString("dd/MM/yyyy");
}
}
}
Parallel Use of Both Serialization methods:
Converter in Two Environment (MVC Core Default Serializer & NewtownSoft)
To Use Converter In two environment using same model, all you have to do, is to apply both attribute from NewtownSoft and Default serializer, these two won't interfere and work fine. Just Make sure your interfaces are correct.
public class MyObject
{
[System.Text.Json.Serialization.JsonConverter(typeof(MsCustomDateTimeConverter))]
[Newtonsoft.Json.JsonConverter(typeof(NsCustomDateTimeConverter))]
public DateTime Date { set; get; }
}
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
var dateTimeConverter = new IsoDateTimeConverter { DateTimeFormat = "dd/MM/yyyy" };
myObject obj = JsonConvert.DeserializeObject<myObject>(myJSONString, dateTimeConverter);
You can also configure it in JsonSerializer:
var serializer = new JsonSerializer
{
DateFormatString = "dd/MM/yyyy"
};