thoth-org/Thoth.Json

caseStrategy doesn't affect DU case names

olivercoad opened this issue · 3 comments

Using auto coders, the caseStrategy parameter doesn't work for discriminated union case names.

Repro:

#r "nuget:Thoth.Json.Net"
open Thoth.Json.Net
type Direction2 = Up | Down | Left | Right
let inline encoder<'T> = Encode.Auto.generateEncoder<'T>(caseStrategy = CamelCase)
printfn "%s" (encoder Left |> Encode.toString 4)

Expected "left" but result is "Left"

The docs say

Decode.Auto helpers accept an optional argument caseStrategy that applies to keys

and DU case names are values in the json not keys, so I guess it's technically not a bug, but it's not what I'd expected.

Hello @olivercoad,

as you found caseStrategy control the keys. It only has effect on the record properties.

If your DUs case don't have any argument, you can convert it into a [<StringEnum>] in Fable.

https://fable.io/docs/communicate/js-from-fable.html#StringEnum

The rules applied to the string enum attributes will be respected:

#if !NETFRAMEWORK
testCase "Encode.Auto.toString works with [<StringEnum>]" <| fun _ ->
let expected = "\"firstPerson\""
let actual = Encode.Auto.toString(0, Camera.FirstPerson)
equal expected actual
testCase "Encode.Auto.toString works with [<StringEnum(CaseRules.LowerFirst)>]" <| fun _ ->
let expected = "\"react\""
let actual = Encode.Auto.toString(0, Framework.React)
equal expected actual
testCase "Encode.Auto.toString works with [<StringEnum(CaseRules.None)>]" <| fun _ ->
let expected = "\"Fsharp\""
let actual = Encode.Auto.toString(0, Language.Fsharp)
equal expected actual
testCase "Encode.Auto.toString works with [<StringEnum>] + [<CompiledName>]" <| fun _ ->
let expected = "\"C#\""
let actual = Encode.Auto.toString(0, Language.Csharp)
equal expected actual
#endif

If you never used String enum here is the types definitions used in the test above:

Thoth.Json/tests/Types.fs

Lines 252 to 268 in 7f6b448

#if !NETFRAMEWORK
[<StringEnum>]
type Camera =
| FirstPerson
| ArcRotate
| IsometricTopDown
[<StringEnum(CaseRules.LowerFirst)>]
type Framework =
| React
| VueJs
[<StringEnum(CaseRules.None)>]
type Language =
| Fsharp
| [<CompiledName("C#")>] Csharp
#endif

Thanks, that works for my use case.

Closing as if a DU has arguments preventing use of StringEnum, then it probably needs a custom encoder anyway if it needs to conform to an existing API spec.