Now in 2024, I am using:
https://github.com/fsprojects/FSharp.Json
It uses System.Text.Json and supports DUs nicely. I configured it like this:
namespace Sample.Json
open System.Text.Json
open System.Text.Json.Serialization
module Json =
let jsonOptions = JsonSerializerOptions()
jsonOptions.DefaultIgnoreCondition <- JsonIgnoreCondition.WhenWritingNull
JsonFSharpConverter(
unionEncoding =
(JsonUnionEncoding.InternalTag
||| JsonUnionEncoding.NamedFields
||| JsonUnionEncoding.UnwrapOption
||| JsonUnionEncoding.AllowUnorderedTag
),
allowOverride = true,
unionTagCaseInsensitive = false
)
|> jsonOptions.Converters.Add
let asJson (obj: 'a) =
JsonSerializer.Serialize(obj, jsonOptions)
let asJsonDocument (obj: 'a) : JsonDocument =
JsonSerializer.Serialize(obj, jsonOptions) |> JsonDocument.Parse
let fromJson<'a> (json: string) =
JsonSerializer.Deserialize<'a>(json, jsonOptions)
The downside is that for some scenarios, like Azure Functions, you can't bind to an F# DU directly. Therefore, I need to convert the object to a JsonDocument and after, convert back and forth using the utility functions above.
On Cosmos, this is a example of the result for a single case DU
type CommandState =
| Created of DateTimeOffset
| Completed of DateTimeOffset
| Failed of failureDate: DateTimeOffset * errorMessage: string * callStack: string option
...
"state": {
"Case": "Created",
"Item": "2024-01-10T16:39:19.3992788-03:00"
},
...
When the field has a name it also uses the name in the json. For me it's working well so far. I hope this helps!