9

I'm trying to do a one way transform from F#'s discriminated union to strings upon serialization instead of the default `"Case": [value]". Being able to deserialize the value again is not an issue. Maybe possible with Json.NET?

// Fsharp 4.1.0
open Newtonsoft.Json // 10.0.3

type HowLame =
| PrettyLame
| SuperLame

type Lame = {
    howLame: HowLame;
}

[<EntryPoint>]
let main argv =
    let lame = { howLame = PrettyLame }
    let ser = JsonConvert.SerializeObject(lame)

    // {"soLame":{"Case":"PrettyLame"}} by default
    printfn "%s" ser

    // Desired
    assert (ser = """{"soLame":"PrettyLame"}""")
    0 // return an integer exit code
wegry
  • 7,046
  • 6
  • 39
  • 59

2 Answers2

9

Creating a custom Json.NET JsonConverter and using it to decorate the discriminated union ("enum style") was enough to get this working the way I wanted. A good chunk of this is transliterated from @Brian Rogers answer in C# https://stackoverflow.com/a/22355712/1924257

open System
open Newtonsoft.Json // 10.0.3
open Newtonsoft.Json.Converters

type ToStringJsonConverter () =
    inherit JsonConverter()
    override this.CanConvert objectType = true;

    override this.WriteJson (writer: JsonWriter, value: obj, serializer: JsonSerializer): unit = 
        writer.WriteValue(value.ToString())

    override this.CanRead = false

    override this.ReadJson (reader: JsonReader, objectType: Type, existingValue: obj, serializer: JsonSerializer) : obj =
        raise (new NotImplementedException());

[<JsonConverter(typeof<ToStringJsonConverter>)>]
type HowLame =
| PrettyLame
| SuperLame

type Lame = {
    howLame: HowLame
}

[<EntryPoint>]
let main argv =
    let lame = { howLame = PrettyLame }
    let ser = JsonConvert.SerializeObject(lame)

    // {"howLame":"PrettyLame"}
    printfn "%s" ser

    0 // return an integer exit code
wegry
  • 7,046
  • 6
  • 39
  • 59
7

If you are willing to make the DU an enum (by specifying explicit values, which probably is OK since there is no 'payload'), you can use the standard StringEnumConverter:

#r "../packages/Newtonsoft.Json/lib/net45/Newtonsoft.Json.dll"
open Newtonsoft.Json

type HowLame = PrettyLame=0 | SuperLame=1
type Lame = { howLame: HowLame; }

// in contrast to DUs, enums must be qualified, i.e. Enum.Value
let lame = { howLame = HowLame.PrettyLame }

let settings = JsonSerializerSettings()
settings.Converters.Add(Converters.StringEnumConverter())

let ser = JsonConvert.SerializeObject(lame, settings)
// val ser : string = "{"howLame":"PrettyLame"}"
CaringDev
  • 8,391
  • 1
  • 24
  • 43
  • 1
    In addition, have a look at [Json.FSharp](https://github.com/haf/Newtonsoft.Json.FSharp), especially the [UnionConverter](https://github.com/haf/Newtonsoft.Json.FSharp/blob/master/src/JsonNet/Converters/UnionConverter.fs) and maybe [Chiron](https://xyncro.tech/chiron/) – CaringDev Nov 13 '17 at 01:47