In order to parse or encode instances into Json you can define static members
For example, given this data type:
type Person = {
Name: string
Age: int
Children: Person list
}
You can map it to JSON like this:
type Person with
static member ToJson (x: Person) =
jobj [
"name" .= x.Name
"age" .= x.Age
"children" .= x.Children
]
let p =
{ Person.Name = "John"
Age = 44
Children =
[
{ Person.Name = "Katy"
Age = 5
Children = [] }
{ Person.Name = "Johnny"
Age = 7
Children = [] }
] }
printfn "%s" (string (toJson p))
And you can map it from JSON like this:
type Person with
static member OfJson json =
match json with
| JObject o ->
let name = o .@ "name"
let age = o .@ "age"
let children = o .@ "children"
match name, age, children with
| Decode.Success name, Decode.Success age, Decode.Success children ->
Decode.Success {
Person.Name = name
Age = age
Children = children
}
| x -> Error <| Uncategorized (sprintf "Error parsing person: %A" x)
| x -> Decode.Fail.objExpected x
let john : Person ParseResult = parseJson """{
"name": "John",
"age": 44,
"children": [{
"name": "Katy",
"age": 5,
"children": []
}, {
"name": "Johnny",
"age": 7,
"children": []
}]
}"""
Though it's much easier to do this in a monadic or applicative way. For example, using FSharpPlus (which is already a dependency of Fleece):
open FSharpPlus
type PersonAp = {
Name: string
Age: int
Children: PersonAp list
}
type PersonAp with
static member Create name age children = { PersonAp.Name = name; Age = age; Children = children }
static member OfJson json =
match json with
| JObject o -> PersonAp.Create <!> (o .@ "name") <*> (o .@ "age") <*> (o .@ "children")
| x -> Decode.Fail.objExpected x
Or monadically:
type PersonM = {
Name: string
Age: int
Children: PersonM list
}
type PersonM with
static member OfJson json =
match json with
| JObject o ->
monad {
let! name = o .@ "name"
let! age = o .@ "age"
let! children = o .@ "children"
return {
Person.Name = name
Age = age
Children = children
}
}
| x -> Decode.Fail.objExpected x
Or you can use the Choice monad/applicative in FSharpx.Extras instead, if you prefer.
You can see more examples in the EdmundsNet project.
namespace System
namespace System.Json
namespace Fleece
module SystemJson
from Fleece
module Operators
from Fleece.SystemJson
type Person =
{ Name: string
Age: int
Children: Person list }
static member OfJson : json:JsonValue -> Result<Person,DecodeError>
static member ToJson : x:Person -> JsonValue
Person.Name: string
Multiple items
val string : value:'T -> string
<summary>Converts the argument to a string using <c>ToString</c>.</summary>
<remarks>For standard integer and floating point values the and any type that implements <c>IFormattable</c><c>ToString</c> conversion uses <c>CultureInfo.InvariantCulture</c>. </remarks>
<param name="value">The input value.</param>
<returns>The converted string.</returns>
--------------------
type string = System.String
<summary>An abbreviation for the CLI type <see cref="T:System.String" />.</summary>
<category>Basic Types</category>
Person.Age: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)
<summary>Converts the argument to signed 32-bit integer. This is a direct conversion for all
primitive numeric types. For strings, the input is converted using <c>Int32.Parse()</c>
with InvariantCulture settings. Otherwise the operation requires an appropriate
static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted int</returns>
--------------------
[<Struct>]
type int = int32
<summary>An abbreviation for the CLI type <see cref="T:System.Int32" />.</summary>
<category>Basic Types</category>
--------------------
type int<'Measure> =
int
<summary>The type of 32-bit signed integer numbers, annotated with a unit of measure. The unit
of measure is erased in compiled code and when values of this type
are analyzed using reflection. The type is representationally equivalent to
<see cref="T:System.Int32" />.</summary>
<category>Basic Types with Units of Measure</category>
Person.Children: Person list
type 'T list = List<'T>
<summary>The type of immutable singly-linked lists. </summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.ListModule" /> module for further operations related to lists.
Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or
the notation <c>[1; 2; 3]</c>. Use the values in the <c>List</c> module to manipulate
values of this type, or pattern match against the values directly.
See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/lists">F# Language Guide - Lists</a>.
</remarks>
type ToJson =
inherit Default1
static member Invoke : x:'t -> JsonValue (requires member ToJson)
static member ToJson : x:bool * ToJson -> JsonValue + 44 overloads
val x : Person
val jobj : x:seq<string * JsonValue> -> JsonValue
<summary>
Creates a new Json object for serialization
</summary>
val p : Person
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
<summary>Print to <c>stdout</c> using the given format, and add a newline.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
val toJson : x:'t -> JsonValue (requires member ToJson)
<summary>
Maps a value to Json
</summary>
type OfJson =
inherit Default1
static member Invoke : x:JsonValue -> 't ParseResult (requires member OfJson)
static member OfJson : decimal * OfJson -> (JsonValue -> Result<decimal,DecodeError>) + 47 overloads
val json : JsonValue
Multiple items
val JObject : x:System.Collections.Generic.IReadOnlyDictionary<string,JsonValue> -> JsonValue
--------------------
active recognizer JObject: JsonValue -> Choice<System.Collections.Generic.IReadOnlyList<JsonValue>,System.Collections.Generic.IReadOnlyDictionary<string,JsonValue>,JsonPrimitive,bool,string,unit>
val o : System.Collections.Generic.IReadOnlyDictionary<string,JsonValue>
val name : Result<string,DecodeError>
val age : Result<int,DecodeError>
val children : Result<Person list,DecodeError>
module Decode
from Fleece.SystemJson
Multiple items
val Success : x:'a -> Result<'a,'b>
--------------------
active recognizer Success: Result<'a,'b> -> Choice<'a,'b>
val name : string
val age : int
val children : Person list
val x : Result<string,DecodeError> * Result<int,DecodeError> * Result<Person list,DecodeError>
union case Result.Error: ErrorValue: 'TError -> Result<'T,'TError>
<summary>
Represents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
</summary>
union case DecodeError.Uncategorized: string -> DecodeError
val sprintf : format:Printf.StringFormat<'T> -> 'T
<summary>Print to a string using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
val x : JsonValue
module Fail
from Fleece.SystemJson.Decode
val objExpected : v:JsonValue -> Result<'t,DecodeError>
val john : Person ParseResult
[<Struct>]
type 'a ParseResult = Result<'a,DecodeError>
val parseJson : x:string -> 'a ParseResult (requires member OfJson)
<summary>
Parses a Json Text and maps to a type
</summary>
namespace FSharpPlus
type PersonAp =
{ Name: string
Age: int
Children: PersonAp list }
static member Create : name:string -> age:int -> children:PersonAp list -> PersonAp
static member OfJson : json:JsonValue -> Result<PersonAp,DecodeError>
PersonAp.Name: string
PersonAp.Age: int
PersonAp.Children: PersonAp list
val children : PersonAp list
static member PersonAp.Create : name:string -> age:int -> children:PersonAp list -> PersonAp
type PersonM =
{ Name: string
Age: int
Children: PersonM list }
static member OfJson : json:JsonValue -> Result<Person,DecodeError>
PersonM.Name: string
PersonM.Age: int
PersonM.Children: PersonM list
val monad<'monad<'t>> : MonadFxBuilder<'monad<'t>>
<summary>
Creates a (lazy) monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information)
</summary>