13

I have a JSON call from an object:

 public record SaveDate(DateOnly StartDate, string EndDate, Object[] objects);
var saveDate= new SaveDate(DateOnly.MinValue, DateTime.MaxValue.ToString("yyyy-MM-dd"), 
new Object[] { objects});

that when executes the API call it ends up returning

{
    "startDate": {
        "year": 1,
        "month": 1,
        "day": 1,
        "dayOfWeek": 1,
        "dayOfYear": 1,
        "dayNumber": 0
    },
    "endDate": "2022-07-07",
    "Object": [
        {
            "foo": "bar"
                }
            ]
        }
    ]
}

however I need to have the format sent from startDate to be the same as endDate ("yyyy-MM-dd") instead of the deserialized version. how can I do that?

note: I'm using DateOnly as type (.net 6.0) and the API expects a string in the format specified above.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Baldie47
  • 1,148
  • 5
  • 16
  • 45
  • What library (e.g., System.Text.Json or Newtonsoft.Json) are you using for serialization? – Kane Feb 07 '22 at 15:48
  • Newtonsoft.Json and Refit – Baldie47 Feb 07 '22 at 15:49
  • Can you apply a custom `IsoDateTimeConverter` attribute as described here? https://stackoverflow.com/questions/18635599/specifying-a-custom-datetime-format-when-serializing-with-json-net – Kane Feb 07 '22 at 15:52
  • I have applied that, however I still get the object {"startDate":{"year":1,"month":1,"day":1,"dayOfWeek":1,"dayOfYear":1,"dayNumber":0}," instead of {"startDate":{"1990-01-01"} – Baldie47 Feb 07 '22 at 16:03
  • Have you checked [this issue](https://github.com/JamesNK/Newtonsoft.Json/issues/2521)? – Peter Csala Feb 07 '22 at 16:08

5 Answers5

20

DateOnly and TimeOnly binding is not fully supported yet. You can implement your own converter for this type:

public class DateOnlyJsonConverter : JsonConverter<DateOnly>
{
    private const string Format = "yyyy-MM-dd";

    public override DateOnly ReadJson(JsonReader reader,
        Type objectType,
        DateOnly existingValue,
        bool hasExistingValue,
        JsonSerializer serializer) =>
        DateOnly.ParseExact((string)reader.Value, Format, CultureInfo.InvariantCulture);

    public override void WriteJson(JsonWriter writer, DateOnly value, JsonSerializer serializer) => 
        writer.WriteValue(value.ToString(Format, CultureInfo.InvariantCulture));
}

class MyClass
{
    [JsonConverter(typeof(DateOnlyJsonConverter))]
    public DateOnly dt { get; set; }
}

// prints {"dt":"2021-01-01"}
Console.WriteLine(JsonConvert.SerializeObject(new MyClass{dt = new DateOnly(2021,1,1)})); 

For System.Text.Json - see this answer.

UPD

Recently released 13.0.2 version of Newtonsoft.Json supports DateOnly and TimeOnly:

13.0.2
New feature - Add support for DateOnly and TimeOnly

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
17

For, .NET 6 I had to update Gugu Stron's answer slightly:

public class DateOnlyJsonConverter : JsonConverter<DateOnly>
{
    private const string Format = "yyyy-MM-dd";

    public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateOnly.ParseExact(reader.GetString(), Format, CultureInfo.InvariantCulture);
    }

    public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(Format, CultureInfo.InvariantCulture));
    }
}

Usage is the same:

class MyClass
{
    [JsonConverter(typeof(DateOnlyJsonConverter))]
    public DateOnly dt { get; set; }
}
Martijn
  • 1,662
  • 15
  • 21
  • 3
    reader.GetString() can be null. Are you sure it is working. It is not for me – maxfromgermany Jul 13 '22 at 21:11
  • 1
    could you please provide which packages you are using – maxfromgermany Jul 13 '22 at 21:49
  • @maxfromgermany He's using `System.Text.Json` and `System.Text.Json.Serialization`. And you're right that `reader.GetString()` can be null; you should check for that and either return an appropriate default value or throw an exception. – Tawab Wakil Jun 06 '23 at 16:12
4

I've run into the problem in an F# project I'm doing. So here is the F# equivalent

type DateOnlyJsonConverter() =
    inherit JsonConverter<DateOnly>()
    let format = "yyyy-MM-dd"

    override this.WriteJson(writer: JsonWriter, value: DateOnly, _: JsonSerializer): unit =
        writer.WriteValue(value.ToString(format, CultureInfo.InvariantCulture))

    override this.ReadJson(reader, _, _, _, _) =
        DateOnly.ParseExact(reader.Value :?> string, format, CultureInfo.InvariantCulture)
        
type TimeOnlyJsonConverter() =
    inherit JsonConverter<TimeOnly>()
    let format = "HH:mm:ss.FFFFFFF"

    override this.WriteJson(writer: JsonWriter, value: TimeOnly, _: JsonSerializer): unit =
        writer.WriteValue(value.ToString(format, CultureInfo.InvariantCulture))
    override this.ReadJson(reader, _, _, _, _) =
        TimeOnly.ParseExact(reader.Value :?> string, format, CultureInfo.InvariantCulture)

How to use:

type Person = {
  Name: string
  BirthDate: DateOnly
}

let json = JsonConvert.SerializeObject(person, Formatting.None, DateOnlyJsonConverter())
(* result:
  { "name": "Ken", "birthDate": "1987-05-20" }
*)
Ken Bonny
  • 729
  • 1
  • 9
  • 29
0

Support for DateOnly and TimeOnly will be/was added in 13.0.2 which is currently in Beta 2. In earlier versions applications should use custom JSON converters.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
0

There is already support for DateOnly and TimeOnly for STJ and Newtonsoft. Update your NuGet packages.

stanimirsp
  • 2,548
  • 2
  • 26
  • 36