The case name respect the CompiledName attributes if provided.
JSON representation
Configuration
When using auto API, you have a very limited control over the JSON representation.
Case strategy
You can decide which case strategy you want to use:
- PascalCase (default)
- CamelCase
- SnakeCase
type Device =
{
EquipmentId : int
SerialNumber : string
}
let myDevice =
{
EquipmentId = 5862
SerialNumber = "1452-48-4298"
}
Encode.Auto.toString(4, myDevice)
// Returns:
// {
// "EquipmentId": "5862",
// "SerialNumber": "1452-48-4298"
// }
Encode.Auto.toString(4, myDevice, caseStrategy = CaseStrategy.CamelCase)
// Returns:
// {
// "equipmentId": "5862",
// "serialNumber": "1452-48-4298"
// }
Encode.Auto.toString(4, myDevice, caseStrategy = CaseStrategy.SnakeCase)
// Returns:
// {
// "equipment_id": "5862",
// "serial_number": "1452-48-4298"
// }When decoding the case strategy is also respected.
Decode.Auto.fromString<Device>(
"""
{
"equipment_id": "5862",
"serial_number": "1452-48-4298"
}
""",
caseStrategy = CaseStrategy.SnakeCase
)
// Returns:
// Ok { EquipmentId = 5862; SerialNumber = "1452-48-4298" }
Decode.Auto.fromString<Device>(
"""
{
"EquipmentId": "5862",
"SerialNumber": "1452-48-4298"
}
""",
caseStrategy = CaseStrategy.SnakeCase
)
// Returns:
// Error
// Error at: `$.serial_number`
// Expecting a string but instead got: undefinedSkip null fields
By default, optional fields are not included in the JSON representation.
type Response<'T> =
{
Code : int
Data : 'T option
}
let response =
{
Code = 200
Data = None
}
Encode.Auto.toString(4, response)
// Returns:
// {
// "Code": "200"
// }If you want to include the Data field, you need to set skipNullField = false
Encode.Auto.toString(4, response, skipNullField = false)
// Returns:
// {
// "Code": "200",
// "Data": null
// }Extra coders
The auto APIs, accept an ExtraCoders objects which is used to extends or override the supported types.
In order to minimize the impact on the bundle size, Thoth.Json don't include out of the box support for the following types:
int64uint64decimalbigint
If you want to use them, you can add them to the ExtraCoders object.
let myExtra =
Extra.empty
|> Extra.withInt64
|> Extra.withUInt64
|> Extra.withDecimal
|> Extra.withBigInt
Encode.Auto.toString(4, 86UL, extra = myExtra)
// Returns:
// "86"Primitives
By default, primitives are represented the same way as they are when using the Manual API.
See Manual API - JSON representation - Numbers for more information.
If the default is not what you want, you can override them by using the extra argument.
let customIntEncoder (value : int) =
Encode.object [
"type", Encode.string "customInt"
"value", Encode.int value
]
let customIntDecoder =
Decode.field "type" Decode.string
|> Decode.andThen (function
| "customInt" ->
Decode.field "value" Decode.int
| _ ->
Decode.fail "Invalid type for customInt"
)
let extra =
Extra.empty
|> Extra.withCustom customIntEncoder customIntDecoder
Encode.Auto.toString(4, 42, extra=extra)
// Returns:
// {
// "type": "customInt",
// "value": 42
// }Records
Records are represented as JSON objects.
type User =
{
Name : string
Age : int
}
Encode.Auto.toString(
4,
{
Name = "Geralt de Riv"
Age = 92
}
)
// Returns:
// {
// "Name": "Geralt de Riv",
// "Age": 92
// }Tuple with no arguments
Tuple without arguments are represented as a string containing the case name:
[<RequireQualifiedAccess>]
type Language =
| FSharp
| [<CompiledName("C#")>] CSharp
Encode.Auto.toString(4, Language.FSharp)
// Returns: "FSharp"
Encode.Auto.toString(4, Language.CSharp)
// Returns: "C#"Tuple with arguments
Tuples are represented using JSON arrays where the first elements is the name of the case followed by as much elements as the tuple arguments.
type MenuElement =
| Label of label : string
| ExternalLink of label : string * url : string
Encode.Auto.toString(4, Label "Introduction")
// Returns:
// [
// "Label",
// "Introduction"
// ]
Encode.Auto.toString(4,
ExternalLink (
label = "Fable",
url = "http://fable.io"
)
)
// Returns:
// [
// "ExternalLink",
// "Fable",
// "http://fable.io"
// ]Option type
In current version of Thoth.Json the option type are erased.
This means that:
Some 42is encoded as42.Noneis encoded asnull.
Class
Classes support need to be added case by case via the extra argument.
This is because Fable offer a limited reflection API and classes are not supported.
type Point(x : int, y : int) =
member __.x with get () = x
member __.y with get () = y
module Point =
let decoder : Decoder<Point> =
Decode.object (fun get ->
Point(
get.Required.Field "x" Decode.int,
get.Required.Field "y" Decode.int
)
)
let encoder (point : Point) =
Encode.object [
"x", Encode.int point.x
"y", Encode.int point.y
]
let myExtra2 =
Extra.empty
|> Extra.withCustom Point.encoder Point.decoder
Encode.Auto.toString(4,
[
Point(1, 2),
Point(3, 4)
],
extra = myExtra2
)
// Returns:
// [
// {
// "x": 1,
// "y": 2
// },
// {
// "x": 3,
// "y": 4
// }
// ]