Piotr Justyna
Piotr Justyna

Reputation: 4966

JSON deserialization: input DateTime? to output Option<DateTime>

Context:

one type has two representations: one in C# and one in F#. Value of the C# type gets serialized to json and then deserialized into a value of the F# type.

Is there a straightforward way to convert null json properties to Option<> F# values?

I'm facing a situation where null C# DateTime? UpdatedDate property gets serialized to json and then is expected to be deserialized into an FSharpOption<DateTime> UpdatedDate value (F# type, C# code, long story...) which breaks the code.

My json looks like this:

{
  "Property1": "1",
  "UpdatedDate": null,
  "Property2": "2"
}

I technically could deserialize my json to the C# type containing DateTime? UpdatedDate and then map the resulting value to the F# type containing FSharpOption<DateTime> UpdatedDate, but surely there must be a better way...


EDIT:

To deserialize json I'm using Newtonsoft's JsonConvert.DeserializeObject and .NET 5 + C# 9.

Upvotes: 1

Views: 233

Answers (2)

Piotr Justyna
Piotr Justyna

Reputation: 4966

Ok, I figured it out thanks to the example @brianberns shared. Sample code:

public class DummyJsonConverter : JsonConverter<FSharpOption<DateTime>>
{
    public override FSharpOption<DateTime> Read(
        ref Utf8JsonReader reader,
        Type typeToConvert,
        JsonSerializerOptions options) =>
        FSharpOption<DateTime>.None;

    public override void Write(
        Utf8JsonWriter writer,
        FSharpOption<DateTime> dateTimeValue,
        JsonSerializerOptions options) =>
        writer.WriteStringValue("");
}

Usage:

var serializerOptions = new JsonSerializerOptions();

serializerOptions.Converters.Add(new DummyJsonConverter());

var whoisResponses = JsonSerializer.Deserialize<SharedSupport.Models.Whois.WhoisResponseV2[]>(
    snsMessage.Message,
    serializerOptions);

The only caveat is that I replaced the Newtonsoft serializer with the System.Text.Json serializer (JsonSerializer).


EDIT:

Following @ruben-bartelink's advice, I used the proposed nuget (FsCodec.NewtonsoftJson) instead of my own custom converter. The code is much simpler now that I don't have to worry about writing my own conversion. Additionally, I don't have to switch to System.Text.Json.

var jsonSerializerSettings = new JsonSerializerSettings();

jsonSerializerSettings.Converters.Add(new OptionConverter());

var whoisResponses = JsonConvert.DeserializeObject<SharedSupport.Models.Whois.WhoisResponseV2[]>(
    snsMessage.Message,
    jsonSerializerSettings);

Upvotes: 2

Brian Berns
Brian Berns

Reputation: 17038

OptionConverter in the Newtonsoft.Json.FSharp package can handle this for you. Your code will look something like this:

let records =
    JsonConvert.DeserializeObject<MyType[]>(
       json,
       [| OptionConverter() :> JsonConverter |])

Upvotes: 2

Related Questions