Fleece


ToJson and OfJson

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>