6

Having this object :

public class Person {
    public string FirstName { get; set; }
    [DataType(DataType.Date)]
    public DateTime Birthday { get; set; }
}

Returning this object as content in Web Api 2 generates this json for Birthday :

"2014-02-20T17:00:32.7114097+00:00"

How can I make it to be : "2014-02-20" without the time part?

Bernard Vander Beken
  • 4,848
  • 5
  • 54
  • 76
Bart Calixto
  • 19,210
  • 11
  • 78
  • 114
  • declare `Birthday` as string :) – L.B Feb 20 '14 at 17:09
  • Lol, unfortunatelly, not an option. :D btw, its working fine right now, but it's ugly to see that on sample code as well on repsonses. api is supposed to be consumed from outside clients and I'm sure they will be asking, what's all about the part after T on dates? :D – Bart Calixto Feb 20 '14 at 17:20
  • Bart, Don't be so sure. It is a well known format used in json's http://en.wikipedia.org/wiki/ISO_8601 – L.B Feb 20 '14 at 17:28
  • @L.B i know, what when the time part it's meaningless I think would be better to make it explicit by not providing it at all. – Bart Calixto Feb 20 '14 at 17:33
  • If you are open to use Json.net I can provide a custom DateTime converter. – L.B Feb 20 '14 at 17:42
  • @L.B Sure!! can a converter be added only when DateTime property have the DataType.Date data annotation? I certainly don't know how to do that and I will really appreciate some guidance on that. – Bart Calixto Feb 20 '14 at 17:50
  • Bard, See the answers. – L.B Feb 20 '14 at 17:59

5 Answers5

11

By far the simplest method is to subclass the built in IsoDateTimeConverter class and set a different DateTimeFormat.

public class IsoDateConverter : IsoDateTimeConverter
{
    public IsoDateConverter() => 
        this.DateTimeFormat = Culture.DateTimeFormat.ShortDatePattern;
}

public class Foo
{
    [JsonConverter(typeof(IsoDateConverter))]
    public DateTimeOffset Date { get; set; }
}
Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311
  • 2
    A nice and clean solution, simpler than the accepted one. Works well for me. – madbadger Jan 10 '18 at 19:05
  • 2
    I would add `Culture.DateTimeFormat.ShortDatePattern` instead of hard coded "yyyy-MM-dd". Thus we can be culture dependent. – managerger Apr 17 '19 at 12:25
  • 3
    If you are using `Culture.DateTimeFormat.ShortDatePattern` you are using the cultures format, not the ISO format. – Jeep Apr 18 '19 at 09:01
10
var json = JsonConvert.SerializeObject(
            new Person() { FirstName = "Joe", Birthday = DateTime.Now.AddDays(-2) },
            new ShortDateConverter()
            );

var p = JsonConvert.DeserializeObject<Person>(json,new ShortDateConverter());

or Decorate your field with [JsonConverter(typeof(ShortDateConverter))]

and use like

var json = JsonConvert.SerializeObject(new Person() 
                     { FirstName = "Joe", Birthday = DateTime.Now.AddDays(-2) } );

var p = JsonConvert.DeserializeObject<Person>(json);

public class ShortDateConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DateTime);
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        return DateTime.ParseExact((string)reader.Value, "yyyy-MM-dd",CultureInfo.InvariantCulture);
    }

    public override void WriteJson(Newtonsoft.Json.JsonWriter writer, object value, Newtonsoft.Json.JsonSerializer serializer)
    {
        DateTime d = (DateTime)value;
        writer.WriteValue(d.ToString("yyyy-MM-dd"));
    }
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • Wow! Works like magic! :D Is there anyway to only override the WriteJson and use the default DateTime converter for the ReadJson ? – Bart Calixto Feb 20 '14 at 18:49
  • @Bart You can try this in ReadJson: `serializer.Converters.Clear(); return serializer.Deserialize(reader);` – L.B Feb 20 '14 at 19:08
  • 1
    if you get 5 more minutes do you mind explain why the clear on converters is needed? Thank you for your time, I feel like im abusing right now :/ Promise, no more questions :D – Bart Calixto Feb 20 '14 at 20:17
4

What about:

public class Person 
{
    public string FirstName { get; set; }

    [JsonIgnore]
    public DateTime Birthday { get; set; }

    public string Birthdate 
    {
        get { return Birthday.ToShortDateString(); }   
        set {}     
    }
}

EDIT: After Habibs comment I changed it to ToShortDateString. If you want another transformation you could use ToString with the format-overload.

This depends on whether you need the whole thing bidirectional. Not sure if the empty setter is needed but I something in mind about that.

Another option could be using Json.NET serializer directly which gives you more power on what is happening including control over DateTime and others.

Alexander Schmidt
  • 5,631
  • 4
  • 39
  • 79
3

For .NET core 3.1 API:

public class ShortDateConverter : JsonConverter<DateTime>
{
    public override DateTime Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options)
    {
        // only supports writing
        throw new NotImplementedException();
    }

    public override void Write(
        Utf8JsonWriter writer,
        DateTime value,
        JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString("yyyy-MM-dd"));
    }
}

And register in Startup.cs in ConfigureServices:

 services.AddMvc()                
            .AddJsonOptions((options) =>
            {
                options.JsonSerializerOptions.Converters.Add(new ShortDateConverter());
            });
 
Aage
  • 5,932
  • 2
  • 32
  • 57
  • 1
    How would it distinguish the DateTime properties that you want serialized with this converter versus those that should be done with the default converter for those fields you want to include time? – Dave Slinn Oct 09 '20 at 06:03
  • @DaveSlinn It wouldn't, this is a solution for this particular question. – Aage Oct 09 '20 at 11:59
  • 1
    You can add it on the individual property in your model: `[JsonConverter(typeof(ShortDateConverter ))] public DateTime Birthday { get; set; }` Don't register ShortDateConverter in startup.cs. This will localize it to only properties you want. – Josh96 Jun 08 '21 at 19:22
0

You should use a proxy property for the serialization and mark the actual property as not serializable: Can you specify format for XmlSerialization of a datetime?

Community
  • 1
  • 1
Sr.PEDRO
  • 1,664
  • 19
  • 21