Search code examples
c#jsonserializationjson.net

Custom converter for deserialization not firing or not hitting break point in Web API


I have a converter like this

class MultiFormatDateConverter : JsonConverter
{
    public List<string> DateTimeFormats { get; set; }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime);
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        string dateString = (string)reader.Value;
        DateTime date;
        foreach (string format in DateTimeFormats)
        {
            // adjust this as necessary to fit your needs
            if (DateTime.TryParseExact(dateString, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out date))
                return date;
        }
        throw new System.Text.Json.JsonException("Unable to parse \"" + dateString + "\" as a date.");
    }
}

and here is the configuration

var settings = new JsonSerializerSettings();
settings.DateParseHandling = DateParseHandling.None;
settings.Converters.Add(new MultiFormatDateConverter
    {
         DateTimeFormats = new List<string> { "yyyyMMddTHHmmssZ", "yyyy-MM-ddTHH:mm","MMMM yyyy","dd/MM/yyyy","dd/MM/yy","MMM-yy","MMM yy" 
     }
 });

and here is how I am calling it:

List<KipReport> rpt730 = JsonConvert.DeserializeObject<List<KipReport>>(responseBody, settings);

This is the JSON and class

[
  {
    "Name":"Alex",
    "MonthWorked":"January 2021",
    "LastEdtDate":"16/02/2021",
    "LastEditBy":"san"
  }
]
class KipReport
{
    public string Name { get; set; }
    public DateTime? MonthWorked { get; set; }
    public DateTime? LastEditDate { get; set; }
}

Mine is a web API and here is the controller which calls the function. Please note it calls the function as Task.Run()

[HttpGet]
public async Task<IActionResult> Get()
{
    await Task.Run(()=>_kReport.GetKReports());
    
    return Accepted();
}

When executing it says

16/03/2021 is not a valid date format

Then I used this way for converting than a converter

var settings = new IsoDateTimeConverter { DateTimeFormat = "dd/MM/yyyy" };

Then error is with January 2021 is not a valid date

Does it means, it's not considering the converter??

Since I have a different format for dates I am using a converter.

So for Web API/Task.Run do we need to do anything specific for the Custom converter?


Solution

  • Your properties are of type DateTime? (i.e. nullable value types) so in CanConvert you must check for objectType == typeof(DateTime?) as well as objectType == typeof(DateTime). Then, in Read(), if the incoming objectType is typeof(DateTime?) you should return null in the event of a null JSON token.

    The following fixed converter does this and also skips comments:

    class MultiFormatDateConverter : JsonConverter
    {
        public List<string> DateTimeFormats { get; set; } = new ();
    
        public override bool CanConvert(Type objectType) => 
            objectType == typeof(DateTime) || objectType == typeof(DateTime?);
    
        public override bool CanWrite => false;
    
        public override void WriteJson(JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer) => throw new NotImplementedException();
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
        {
            if (reader.MoveToContent().TokenType == JsonToken.Null)
                return objectType == typeof(DateTime?) ? null : throw new System.Text.Json.JsonException("Unable to parse null as a date.");
            else if (reader.TokenType != JsonToken.String)
                throw new System.Text.Json.JsonException("Unable to parse token \"" + reader.TokenType + "\" as a date.");
            string dateString = (string)reader.Value;
            foreach (string format in DateTimeFormats)
            {
                // adjust this as necessary to fit your needs
                if (DateTime.TryParseExact(dateString, format, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
                    return date;
            }
            throw new System.Text.Json.JsonException("Unable to parse \"" + dateString + "\" as a date.");
        }
    }
    
    public static partial class JsonExtensions
    {
        public static JsonReader MoveToContent(this JsonReader reader)
        {
            while ((reader.TokenType == JsonToken.Comment || reader.TokenType == JsonToken.None) && reader.Read())
                ;
            return reader;
        }
    }
    

    Notes:

    • In your JSON you have a property named "LastEdtDate" while the corresponding c# property is LastEditDate. The JSON property name is missing the letter i in Edit and so will not get bound to the c# property. I assume this is a typo in the question, but if not, you will need to add [JsonProperty("LastEdtDate")] to LastEditDate.

    Demo fiddle here.