All Projects → fsprojects → Fleece

fsprojects / Fleece

Licence: apache-2.0
Json mapper for F#

Labels

Projects that are alternatives of or similar to Fleece

Noproto
Flexible, Fast & Compact Serialization with RPC
Stars: ✭ 138 (-4.17%)
Mutual labels:  json
Bricks
A standard library for microservices.
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Generatedata
A powerful, feature-rich, random test data generator.
Stars: ✭ 1,883 (+1207.64%)
Mutual labels:  json
Json Autotype
Automatic Haskell type inference from JSON input
Stars: ✭ 139 (-3.47%)
Mutual labels:  json
Dirty Json
A parser for invalid JSON
Stars: ✭ 141 (-2.08%)
Mutual labels:  json
Hugejsonviewer
Viewer for JSON files that can be GBs large.
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Cfgdiff
diff(1) all your configs
Stars: ✭ 138 (-4.17%)
Mutual labels:  json
Fsharp.json
F# JSON Reflection based serialization library
Stars: ✭ 144 (+0%)
Mutual labels:  json
Jsonc
JSON with comments for Go!
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Jsonrpc
The jsonrpc package helps implement of JSON-RPC 2.0
Stars: ✭ 143 (-0.69%)
Mutual labels:  json
Criterion
Microbenchmarking for Modern C++
Stars: ✭ 140 (-2.78%)
Mutual labels:  json
Fig
A minimalist Go configuration library
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Funiture
慕课网课程推荐 Java并发编程与高并发解决方案:http://coding.imooc.com/class/195.html Java开发企业级权限管理系统:http://coding.imooc.com/class/149.html github: https://github.com/kanwangzjm/funiture, spring项目,权限管理、系统监控、定时任务动态调整、qps限制、sql监控(邮件)、验证码服务、短链接服务、动态配置等
Stars: ✭ 1,786 (+1140.28%)
Mutual labels:  json
Yii2 Json Api
Implementation of JSON API specification for the Yii framework
Stars: ✭ 139 (-3.47%)
Mutual labels:  json
React
JSON powered forms for React.js
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Packages
📦 Package configurations - The #1 free and open source CDN built to make life easier for developers.
Stars: ✭ 139 (-3.47%)
Mutual labels:  json
Smoke
💨 Simple yet powerful file-based mock server with recording abilities
Stars: ✭ 142 (-1.39%)
Mutual labels:  json
Ti Rpc
基于swoole封装的一个简易的JSON协议的RPC框架,思路是借鉴的,代码是自己写的。小修小改的,目前服务于前公司(注意是前公司)生产环境,每日支撑大约8000万次调用。
Stars: ✭ 144 (+0%)
Mutual labels:  json
Restclient.net
.NET REST Client Framework for all platforms
Stars: ✭ 143 (-0.69%)
Mutual labels:  json
Json.prune
A pruning version of JSON.stringify, allowing for example to stringify window or any big or recursive object
Stars: ✭ 142 (-1.39%)
Mutual labels:  json

Fleece

Fleece is a JSON mapper for F#. It simplifies mapping from a Json library's JsonValue onto your types, and mapping from your types onto JsonValue.

The Json library could be System.Json, System.Text.Json, FSharp.Data's or NewtonSoft's Json.NET.

Its design is strongly influenced by Haskell's Aeson. Like Aeson, Fleece is designed around two typeclasses (in FSharpPlus style) ToJson and OfJson.

Download binaries

Example

For example, given this data type:

type Person = {
    Name: string
    Age: int
    Children: Person list
}

You can map it to JSON like this:

open System.Json
open Fleece.SystemJson
open Fleece.SystemJson.Operators

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 [email protected] "name"
            let age = o [email protected] "age"
            let children = o [email protected] "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 Person with
    static member Create name age children = { Person.Name = name; Age = age; Children = children }

    static member OfJson json =
        match json with
        | JObject o -> Person.Create <!> (o [email protected] "name") <*> (o [email protected] "age") <*> (o [email protected] "children")
        | x -> Decode.Fail.objExpected x

Or monadically:

type Person with
    static member OfJson json =
        match json with
        | JObject o -> 
            monad {
                let! name = o [email protected] "name"
                let! age = o [email protected] "age"
                let! children = o [email protected] "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.

CODEC

For types that deserialize to Json Objets, typically (but not limited to) records, you can alternatively use codecs and have a single method which maps between fields and values.

type Person = { 
    name : string * string
    age : int option
    children: Person list } 
    with
    static member JsonObjCodec =
        fun f l a c -> { name = (f, l); age = a; children = c }
        <!> jreq  "firstName" (Some << fun x -> fst x.name)
        <*> jreq  "lastName"  (Some << fun x -> snd x.name)
        <*> jopt  "age"       (fun x -> x.age) // Optional fields: use 'jopt'
        <*> jreq  "children"  (fun x -> Some x.children)


let p = {name = ("John", "Doe"); age = None; children = [{name = ("Johnny", "Doe"); age = Some 21; children = []}]}
printfn "%s" (string (toJson p))

let john = parseJson<Person> """{"children": [{"children": [],"age": 21,"lastName": "Doe","firstName": "Johnny"}],"lastName": "Doe","firstName": "John"}"""

If you prefer you can write the same with functions:

type Person = { 
    name : string * string
    age : int option
    children: Person list }
    with
    static member JsonObjCodec =
        fun f l a c -> { name = (f, l); age = a; children = c }
        |> withFields
        |> jfield    "firstName" (fun x -> fst x.name)
        |> jfield    "lastName"  (fun x -> snd x.name)
        |> jfieldOpt "age"       (fun x -> x.age)
        |> jfield    "children"  (fun x -> x.children)

Discriminated unions can be modeled with alternatives:

type Shape =
    | Rectangle of width : float * length : float
    | Circle of radius : float
    | Prism of width : float * float * height : float
    with 
        static member JsonObjCodec =
            Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None)
            <|> ( Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None) )
            <|> ( Prism <!> jreq "prism"   (function Prism (x, y, z) -> Some (x, y, z) | _ -> None) )

or using the jchoice combinator:

type Shape with
        static member JsonObjCodec =
            jchoice
                [
                    Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None)
                    Circle    <!> jreq "radius"    (function Circle x -> Some x | _ -> None)
                    Prism     <!> jreq "prism"     (function Prism (x, y, z) -> Some (x, y, z) | _ -> None)
                ]

What's happening here is that we're getting a Codec to/from a Json Object (not neccesarily a JsonValue) which Fleece is able to take it and fill the gap by composing it with a codec from JsonObject to/from JsonValue.

We can also do that by hand, we can manipulate codecs by using functions in the Codec module. Here's an example:

open System.Text

let personBytesCodec =
    Person.JsonObjCodec
    |> Codec.compose jsonObjToValueCodec    // this is the codec that fills the gap to/from JsonValue
    |> Codec.compose jsonValueToTextCodec   // this is a codec between JsonValue and JsonText
    |> Codec.invmap Encoding.UTF8.GetString Encoding.UTF8.GetBytes    // This is a pair of of isomorphic functions

let bytePerson = Codec.encode personBytesCodec p
// val bytePerson : byte [] = [|123uy; 13uy; 10uy; 32uy; 32uy; ... |]
let p' = Codec.decode personBytesCodec bytePerson

Combinators

So far we've seen how Fleece is capable of encoding/decoding by deriving automatically a codec from static members in the type.

But for those cases where we don't have control over the types (extension members won't be taken into account) we can explicitly specify combinators.

To do so, a set of the available functions exists, ending with the With suffix, which accepts a combinator as first parameter:

type Color = Red | Blue | White

type Car = {
    Id : string
    Color : Color
    Kms : int }

let colorDecoder = function
    | JString "red"   -> Decode.Success Red  
    | JString "blue"  -> Decode.Success Blue 
    | JString "white" -> Decode.Success White
    | JString  x as v -> Decode.Fail.invalidValue v ("Wrong color: " + x)
    | x               -> Decode.Fail.strExpected  x

let colorEncoder = function
    | Red   -> JString "red"
    | Blue  -> JString "blue"
    | White -> JString "white"

let colorCodec = colorDecoder, colorEncoder
    
let [<GeneralizableValue>]carCodec<'t> =
    fun i c k -> { Id = i; Color = c; Kms = k }
    |> withFields
    |> jfieldWith JsonCodec.string "id"    (fun x -> x.Id)
    |> jfieldWith colorCodec       "color" (fun x -> x.Color)
    |> jfieldWith JsonCodec.int    "kms"   (fun x -> x.Kms)
    |> Codec.compose jsonObjToValueCodec

let car = { Id = "xyz"; Color = Red; Kms = 0 }

let jsonCar = Codec.encode carCodec car
// val jsonCar : JsonValue = {"id": "xyz", "color": "red", "kms": 0}

Maintainer(s)

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].