Skip to content

Objects

The objectCodec computation expression builds a codec for a record.

Each Codec.field names a JSON property, says how to read that property out of the record, and gives the codec for its value. The return rebuilds the record from the decoded fields.

open Thoth.Json.Core
open Thoth.Json.JavaScript

type User =
    {
        Name: string
        Age: int
    }

module User =

    let codec: Codec<User> =
        objectCodec {
            let! name = Codec.field "name" _.Name Codec.string
            and! age = Codec.field "age" _.Age Codec.int

            return
                {
                    Name = name
                    Age = age
                }
        }

let json =
    {
        Name = "Geralt de Riv"
        Age = 92
    }
    |> Encode.codec User.codec
    |> Encode.toString 4

printfn "%s" json

Decode.fromString User.codec json |> Docs.print

Use and! between fields. They are decoded independently, so all of them are read whatever the others do.

Optional fields

Codec.optional reads a 'T option. The property is omitted when the value is None, and its absence decodes back to None.

open Thoth.Json.Core
open Thoth.Json.JavaScript

type User =
    {
        Name: string
        Nickname: string option
    }

module User =

    let codec: Codec<User> =
        objectCodec {
            let! name = Codec.field "name" _.Name Codec.string
            and! nickname = Codec.optional "nickname" _.Nickname Codec.string

            return
                {
                    Name = name
                    Nickname = nickname
                }
        }

{
    Name = "Geralt de Riv"
    Nickname = None
}
|> Encode.codec User.codec
|> Encode.toString 0
|> printfn "%s"

Decode.fromString User.codec """{ "name": "Geralt de Riv" }""" |> Docs.print

Nesting

A field takes any codec, including one built by another objectCodec.

open Thoth.Json.Core
open Thoth.Json.JavaScript

type Author =
    {
        Name: string
    }

type Post =
    {
        Title: string
        Author: Author
    }

module Author =

    let codec: Codec<Author> =
        objectCodec {
            let! name = Codec.field "name" _.Name Codec.string

            return
                {
                    Name = name
                }
        }

module Post =

    let codec: Codec<Post> =
        objectCodec {
            let! title = Codec.field "title" _.Title Codec.string
            and! author = Codec.field "author" _.Author Author.codec

            return
                {
                    Title = title
                    Author = author
                }
        }

{
    Title = "Handle JSON with Fable"
    Author =
        {
            Name = "Triss Merigold"
        }
}
|> Encode.codec Post.codec
|> Encode.toString 4
|> printfn "%s"

Reaching into a path

Codec.at moves a whole codec under a path of property names. Decoding reads the value at that path, encoding wraps it back in nested objects.

open Thoth.Json.Core
open Thoth.Json.JavaScript

let codec = Codec.at [ "data"; "count" ] Codec.int

Encode.codec codec 42 |> Encode.toString 0 |> printfn "%s"

Decode.fromString codec """{ "data": { "count": 42 } }""" |> Docs.print
Edit this page