Serialize JSON from object to string / DateOnly

Viewed 3246

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.

3 Answers

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, .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; }
}

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" }
*)
Related