0

We have a API service that we are updating and we converted some date objects from strings to DateTime objects. In the old code we tested the string if it would parse to a data time or not. If it was a bad formatted string, it would assign DateTime.Min and continue on. Now customers are sending in bad dates and it blows up since the serialization happens outside our code (MVC Controller). I am trying to find some way that when serializing a DateTime object, if it can not parse it, it just returns DateTime.Min instead of blowing up the call.

Here is the response from the API Call.

{
  "date": [
    "Could not convert string to DateTime: Invalid Date. Path 'date', line 3, position 24."
  ]
}

===== UPDATE =====

I finally found somewhere that recommended a custom JsonConverter. I finally got something that works, but there is little out there so if there is something I could do better I am all ears.

Custom Converter

  public class DateConverter : JsonConverter
  {
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
      try
      {
        return DateTime.Parse(reader.Value.ToString());
      }
      catch (Exception ex)
      {
        return DateTime.MinValue;
      }
    }

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

DTO Class

public class Request
  {

    [JsonConverter(typeof(SafeDateConverter))]
    public DateTime Date { get; set; }
  }
Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
Brandon Turpy
  • 883
  • 1
  • 10
  • 32
  • Please provide a [Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) – marcusturewicz Nov 15 '18 at 00:10
  • What code would you like me to include? This error is happening before it even hits any of the code that I write. I send in bad date, it gives error. Examples of those are irrelevant. I need to figure out how to override the default settings to gracefully handle this error and unfortunately I do not know where go... hence why I am reaching out for help. I hate posting on here because everyone criticizes and is down vote happy instead of actually trying to help. – Brandon Turpy Nov 15 '18 at 00:27
  • "I am trying to find some way that when serializing a DateTime object, if it can not parse it, it just returns DateTime.Min instead of blowing up the call.". What have you tried so far? – marcusturewicz Nov 15 '18 at 00:33
  • Searching online for a solution and everywhere I find says to wrap it in a try catch which I am not able to do as explained in the question. Do I have to prove due diligence to get an answer here? I guess if you do not understand the question I would get it, but if I am asked a question, if I understand the question I answer it. – Brandon Turpy Nov 15 '18 at 00:37
  • It is hard to help without seeing any code. Can you show what your controller action looks like? – marcusturewicz Nov 15 '18 at 00:43
  • I can, but I am having a hard time figuring out why it would help to post code that is not effecting the issue. The issue is Newtonsoft and how it serializes BEFORE it hits the controller. I guess I feel like that goes against the "Minimal" part by providing irrelevant code. – Brandon Turpy Nov 15 '18 at 00:48
  • Please do if you don't mind - I'm still trying to grasp the situation and this is the code closest to the problem. – marcusturewicz Nov 15 '18 at 00:53
  • You could possibly use Validation with data annotations on your DTO, [like in this answer](https://stackoverflow.com/a/16567020/6939988). Then in your controller, check `if (!ModelState.IsValid)` then return your `DateTime.Min`...? – marcusturewicz Nov 15 '18 at 00:58

1 Answers1

1

Another approach introduce another property on deserialized class of type DateTime? and leave original string property as it is.

public class Request
{
    public string Date { get; set; }

    private DateTime? _parsedDate;
    [JsonIgnore]
    public DateTime? ParsedDate 
    { 
        get { return _parsedDate; } 
        set
        {
            if (DateTime.TryParse(value, out DateTime parsed)
            {
                _parsedDate = parsed;
                return;
            }

            _parsed = null;
        }
    }       
}

But having custom serializer looks better, because you need change nothing in the code which already uses deserialized object.

Suggestion:
Do not use try ... catch for serializing bad formatted dates, there is DateTime.TryParse method which will do this without throwing exceptions.

And if it is not late, you can use Nullable<DateTime> instead of having DateTime.Min as "not existing" value.

Fabio
  • 31,528
  • 4
  • 33
  • 72