18

I'd like DateTime fields that are set to DateTime.MinValue returned by my Web API to be serialized to NULL instead of "0001-01-01T00:00:00".

I understand there's a way to get JSON.NET to omit fields that are set to default values, but I would prefer JSON.NET to specifically serialize DateTime MinValue / "0001-01-01T00:00:00" as null.

Is there a way to do this?

Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
Mick
  • 6,527
  • 4
  • 52
  • 67
  • you can set in property itself. if its DateTime.MinValue. Remeber your DateTime should be nullable. – Vivek Nuna Oct 26 '16 at 07:38
  • It's probably a mistake but we have a code generation system which generates Entity code for NOT NULL DateTime columns as 'DateTime'. If the column is Nullable the .NET type is 'DateTime?'. I don't think I could change this without breaking a lot of code – Mick Oct 26 '16 at 07:41
  • But without making it nullable you cannot assign null value to DateTime – Vivek Nuna Oct 26 '16 at 07:42
  • You can write a custom `DateTimeConverter` that inherits of `Newtonsoft.Json.Converters.DateTimeConverterBase` to use as a parameter of the `DeserializeObject` method : http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Converters_DateTimeConverterBase.htm – Béranger Oct 26 '16 at 07:44
  • I would presume if you have a property "MyDate" with a type DateTime and the you have JSON "MyDate" : NULL, JSON.NET will set MyDate to default. I'm only wanting the serialization of the field to be null – Mick Oct 26 '16 at 07:44

3 Answers3

24

Create a custom converter which serializes DateTime.MinValue into null, and (if required) deserializes null into DateTime.MinValue:

public class MinDateTimeConverter : DateTimeConverterBase
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.Value == null)
            return DateTime.MinValue;

        return (DateTime)reader.Value;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        DateTime dateTimeValue = (DateTime)value;
        if (dateTimeValue == DateTime.MinValue)
        {
            writer.WriteNull();
            return;
        }

        writer.WriteValue(value);
    }
}

You can then use attributes to add the converter to your data class, as shown in this example:

public class Example
{
    [JsonConverter(typeof(MinDateTimeConverter))]
    public DateTime ValueOne { get; set; }

    [JsonConverter(typeof(MinDateTimeConverter))]
    public DateTime ValueTwo { get; set; }
}

public static void Main(string[] args)
{
    Example data = new Example();
    data.ValueOne = DateTime.MinValue;
    data.ValueTwo = DateTime.Now;

    JsonSerializer serializer = new JsonSerializer();

    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, data);
        Console.Write(writer.ToString());
    }

    Console.ReadKey();
}

Console output:

{"ValueOne":null,"ValueTwo":"2016-10-26T09:54:48.497463+01:00"}
Alsty
  • 817
  • 10
  • 23
  • [`DateTimeConverterBase`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/DateTimeConverterBase.cs) also accepts `DateTimeOffset` so you need to modify `CanConvert` to only return true for `DateTime` and `DateTime?` – dbc Oct 26 '16 at 22:08
  • @dbc This converter will only work when directly applied to `DateTime` properties using a `JsonConverter` attribute. `CanConvert` is not called in this scenario. `ReadJson` would need to be modified if `DateTime?` types need to be supported. – Alsty Oct 27 '16 at 08:25
7

Custom DateTime Json Converter

public class DateTimeConverter : JsonConverter
{   
    public override void WriteJson(JsonWriter jsonWriter, object inputObject,JsonSerializer jsonSerializer)
    {
        // Typecast the input object
        var dateTimeObject = inputObject as DateTime?;

        // Set the properties of the Json Writer
        jsonWriter.Formatting = Newtonsoft.Json.Formatting.Indented;

        if(dateTimeObject == DateTime.MinValue)
            jsonWriter.WriteValue((DateTime?)null);
        else
            jsonWriter.WriteValue(dateTimeObject);
    }


    public override object ReadJson(JsonReader reader,
        Type objectType,
        object existingValue,
        JsonSerializer serializer)
    {
        DateTime? readValue = reader.ReadAsDateTime();

        return (readValue == null) ? DateTime.MinValue : readValue;     

    }


    public override bool CanConvert(Type objectType)
    {
        return typeof(DateTime?).IsAssignableFrom(objectType);
    }
}

Usage:

public class DateTest
{
    [JsonConverterAttribute(typeof(DateTimeConverter))]
    public DateTime? MyDateTime { get; set;}

    [JsonConverterAttribute(typeof(DateTimeConverter))]
    public DateTime? MyDateTime1 { get; set; }
}

void Main()
{
    DateTest dateTest = new DateTest 
    { 
      MyDateTime = DateTime.MinValue, 
      MyDateTime1 = DateTime.MaxValue
    };

    Console.WriteLine(JsonConvert.SerializeObject(dateTest));
}

Result:

{
  "MyDateTime": null,
  "MyDateTime1": "9999-12-31T23:59:59.9999999"
}
Mrinal Kamboj
  • 11,300
  • 5
  • 40
  • 74
2

Not 100% clear on why the original poster wanted this and it's 5 years down the line but I too wanted to achieve this.

However, in my case the issue was that when I serialised my object it was inserting the DateTime fields because they hadn't been specified on the incoming object but were being returned as MinValue not null.

Thus when I deserialised at the other end these fields were showing as 'dirty' and being parsed.

In this case you can just amend your JsonSerializerSettings block to include 'ignore' in the default value handling:

string serialisedMessage = JsonConvert.SerializeObject(message, new JsonSerializerSettings
        {
            NullValueHandling = NullValueHandling.Ignore,
            DefaultValueHandling = DefaultValueHandling.Ignore
        });

So I'm leaving this in case of future users who are in my position.

TheoGB
  • 43
  • 7