Thoth.Json
Thoth.Json copied to clipboard
Add example of decoder/encoder usage for simple and complexe F# types
Issue by MangelMaxime
Friday Nov 16, 2018 at 16:16 GMT
Originally opened as https://github.com/MangelMaxime/Thoth/issues/72
Current needs:
- [ ] Show how to encode/decode DUs with and without cases
type Foo =
{ Name : string }
static member Decoder =
Decode.object (fun get ->
{ Name = get.Required.Field "name" Decode.string }
)
static member Encoder (x : Foo) =
Encode.object [
"name", Encode.string x.Name
]
type Bar =
{ Value : int }
static member Decoder =
Decode.object (fun get ->
{ Value = get.Required.Field "value" Decode.int }
)
static member Encoder (x : Bar) =
Encode.object [
"value", Encode.int x.Value
]
type MyDUWrapper =
| FooWrapper of List<Foo>
| BarWrapper of List<Bar>
static member Decoder =
Decode.object (fun get ->
let typ = get.Required.Raw (Decode.field "type" Decode.string)
match typ with
| "FooWrapper" ->
get.Required.Field "values" (Decode.list Foo.Decoder)
|> FooWrapper
| "BarWrapper" ->
get.Required.Field "values" (Decode.list Bar.Decoder)
|> BarWrapper
| unkown ->
failwithf "`%s` isn't a valid type for MyDUWrapper" unkown
)
static member Encoder (x : MyDUWrapper) =
match x with
| FooWrapper fooValues ->
Encode.object [
"type", Encode.string "FooWrapper"
"values", fooValues |> List.map Foo.Encoder |> Encode.list
]
| BarWrapper barValues ->
Encode.object [
"type", Encode.string "BarWrapper"
"values", barValues |> List.map Bar.Encoder |> Encode.list
]
let fooList =
[ { Name = "Maxime" }
{ Name = "Robert" }
{ Name = "Hervé" } ]
let barList =
[ { Value = 27 }
{ Value = 17 }
{ Value = 227 }
{ Value = 257 } ]
let fooListJson =
fooList
|> List.map Foo.Encoder
|> Encode.list
|> Encode.toString 4
let barListJson =
barList
|> List.map Bar.Encoder
|> Encode.list
|> Encode.toString 4
let myDUWrapperFooJson =
FooWrapper fooList
|> MyDUWrapper.Encoder
|> Encode.toString 4
let myDUWrapperBarJson =
BarWrapper barList
|> MyDUWrapper.Encoder
|> Encode.toString 4
match Decode.fromString MyDUWrapper.Decoder myDUWrapperFooJson with
| Ok values ->
Browser.console.log values
| Error msg -> Browser.console.error msg
match Decode.fromString MyDUWrapper.Decoder myDUWrapperBarJson with
| Ok values ->
Browser.console.log values
| Error msg -> Browser.console.error msg
Comment by MangelMaxime
Friday Nov 23, 2018 at 13:25 GMT
Show how to encode/decode DUs with and without cases
open Thoth.Json
open Fable.Import
type Info =
{ Firstname : string
Age : int }
static member Decoder =
Decode.object (fun get ->
{ Firstname = get.Required.Field "firstname" Decode.string
Age = get.Required.Field "age" Decode.int }
)
static member Encoder (info : Info) =
Encode.object [
"firstname" , Encode.string info.Firstname
"age", Encode.int info.Age
]
type Test =
| CaseA
| CaseB of string
| CaseC of Info
| CaseD of int * string
static member Decoder =
Decode.field "type" Decode.string
|> Decode.andThen (
function
| "caseA" ->
Decode.succeed CaseA
| "caseB" ->
Decode.field "data" Decode.string
|> Decode.map CaseB
| "caseC" ->
Decode.field "data" Info.Decoder
|> Decode.map CaseC
| "caseD" ->
Decode.tuple2 Decode.int Decode.string
|> Decode.field "data"
|> Decode.map CaseD
| unkown ->
sprintf "`%s` is an unkown type" unkown
|> Decode.fail
)
static member Encoder (v : Test) =
let typ, data =
match v with
| CaseA ->
"caseA", Encode.nil
| CaseB txt ->
"caseB", Encode.string txt
| CaseC info ->
"caseC", Info.Encoder info
| CaseD (age, name) ->
"caseD", Encode.tuple2 Encode.int Encode.string (age, name)
Encode.object [
"type", Encode.string typ
"data", data
]
let jsonCaseA =
CaseA
|> Test.Encoder
|> Encode.toString 4
printfn "JsonCaseA:\n%s" jsonCaseA
Decode.fromString Test.Decoder jsonCaseA
|> Browser.console.log
let jsonCaseB =
CaseB "maxime"
|> Test.Encoder
|> Encode.toString 4
printfn "JsonCaseB:\n%s" jsonCaseB
Decode.fromString Test.Decoder jsonCaseB
|> Browser.console.log
let jsonCaseC =
CaseC { Firstname = "Maxime"; Age = 26 }
|> Test.Encoder
|> Encode.toString 4
printfn "JsonCaseC:\n%s" jsonCaseC
Decode.fromString Test.Decoder jsonCaseC
|> Browser.console.log
let jsonCaseD =
CaseD (26, "Maxime")
|> Test.Encoder
|> Encode.toString 4
printfn "JsonCaseD:\n%s" jsonCaseD
Decode.fromString Test.Decoder jsonCaseD
|> Browser.console.log
Comment by MangelMaxime
Friday Nov 23, 2018 at 14:18 GMT
For DUs we could use Decode.onfOf
to keep the JSON compact and also make it readible for humans.
static member Decode =
Decode.oneOf [
Decode.field "Contains" (Decode.succeed Contains)
Decode.field "Defined" (Decode.string |> Decode.map Defined)
Decode.field "NotDefined" (Decode.tuple2 Decode.int Decode.int |> Decode.map NotDefined)
]