Skip to content

Commit 9429586

Browse files
authored
Readme for 0.10.0 (#119)
1 parent 9f65bf6 commit 9429586

File tree

1 file changed

+91
-51
lines changed

1 file changed

+91
-51
lines changed

README.md

Lines changed: 91 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The Json library could be [System.Json](http://bit.ly/1axIBoA), [System.Text.Jso
88
Its design is strongly influenced by Haskell's [Aeson](http://hackage.haskell.org/package/aeson-0.7.0.0/docs/Data-Aeson.html). Like Aeson, Fleece is designed around two typeclasses (in [FSharpPlus](https://github.com/fsprojects/FSharpPlus) style) ToJson and OfJson.
99

1010
### Download binaries
11-
11+
* [Fleece core](https://www.nuget.org/packages/Fleece)
1212
* [For System.Json](https://www.nuget.org/packages/Fleece.SystemJson/)
1313
* [For System.Text.Json](https://www.nuget.org/packages/Fleece.SystemTextJson/)
1414
* [For FSharp.Data](https://www.nuget.org/packages/Fleece.FSharpData/)
@@ -29,9 +29,8 @@ type Person = {
2929
You can map it to JSON like this:
3030

3131
```fsharp
32-
open System.Json
33-
open Fleece.SystemJson
34-
open Fleece.SystemJson.Operators
32+
open Fleece
33+
open Fleece.Operators
3534
3635
type Person with
3736
static member ToJson (x: Person) =
@@ -54,7 +53,10 @@ let p =
5453
Children = [] }
5554
] }
5655
57-
printfn "%s" (string (toJson p))
56+
// Test with System.Text.Json
57+
58+
open Fleece.SystemTextJson
59+
printfn "%s" (toJsonText p)
5860
```
5961

6062
And you can map it from JSON like this:
@@ -77,7 +79,7 @@ type Person with
7779
| x -> Error <| Uncategorized (sprintf "Error parsing person: %A" x)
7880
| x -> Decode.Fail.objExpected x
7981
80-
let john : Person ParseResult = parseJson """{"name": "John", "age": 44, "children": [{"name": "Katy", "age": 5, "children": []}, {"name": "Johnny", "age": 7, "children": []}]}"""
82+
let john : Person ParseResult = ofJsonText """{"name": "John", "age": 44, "children": [{"name": "Katy", "age": 5, "children": []}, {"name": "Johnny", "age": 7, "children": []}]}"""
8183
```
8284

8385
Though it's much easier to do this in a monadic or applicative way. For example, using [FSharpPlus](https://github.com/fsprojects/FSharpPlus) (which is already a dependency of Fleece):
@@ -95,18 +97,21 @@ type Person with
9597
9698
```
9799

98-
Or monadically:
100+
or with applicatives:
99101

100102

101103
```fsharp
104+
105+
open FSharpPlus
106+
102107
type Person with
103108
static member OfJson json =
104109
match json with
105110
| JObject o ->
106111
monad {
107112
let! name = o .@ "name"
108-
let! age = o .@ "age"
109-
let! children = o .@ "children"
113+
and! age = o .@ "age"
114+
and! children = o .@ "children"
110115
return {
111116
Person.Name = name
112117
Age = age
@@ -133,37 +138,17 @@ type Person = {
133138
age : int option
134139
children: Person list }
135140
with
136-
static member JsonObjCodec =
141+
static member get_Codec () =
137142
fun f l a c -> { name = (f, l); age = a; children = c }
138-
<!> jreq "firstName" (Some << fun x -> fst x.name)
139-
<*> jreq "lastName" (Some << fun x -> snd x.name)
140-
<*> jopt "age" (fun x -> x.age) // Optional fields: use 'jopt'
141-
<*> jreq "children" (fun x -> Some x.children)
142-
143-
144-
let p = {name = ("John", "Doe"); age = None; children = [{name = ("Johnny", "Doe"); age = Some 21; children = []}]}
145-
printfn "%s" (string (toJson p))
146-
147-
let john = parseJson<Person> """{"children": [{"children": [],"age": 21,"lastName": "Doe","firstName": "Johnny"}],"lastName": "Doe","firstName": "John"}"""
143+
<!> jreq "firstName" (Some << fun x -> fst x.name)
144+
<*> jreq "lastName" (Some << fun x -> snd x.name)
145+
<*> jopt "age" (fun x -> x.age) // Optional fields can use 'jopt'
146+
<*> jreq "children" (fun x -> Some x.children)
147+
|> ofObjCodec
148+
149+
let john: Person ParseResult = ofJsonText """{"name": "John", "age": 44, "children": [{"name": "Katy", "age": 5, "children": []}, {"name": "Johnny", "age": 7, "children": []}]}"""
148150
```
149151

150-
If you prefer you can write the same with functions:
151-
152-
```fsharp
153-
154-
type Person = {
155-
name : string * string
156-
age : int option
157-
children: Person list }
158-
with
159-
static member JsonObjCodec =
160-
fun f l a c -> { name = (f, l); age = a; children = c }
161-
|> withFields
162-
|> jfield "firstName" (fun x -> fst x.name)
163-
|> jfield "lastName" (fun x -> snd x.name)
164-
|> jfieldOpt "age" (fun x -> x.age)
165-
|> jfield "children" (fun x -> x.children)
166-
```
167152

168153
Discriminated unions can be modeled with alternatives:
169154
```fsharp
@@ -172,11 +157,14 @@ type Shape =
172157
| Circle of radius : float
173158
| Prism of width : float * float * height : float
174159
with
175-
static member JsonObjCodec =
176-
Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None)
177-
<|> ( Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None) )
178-
<|> ( Prism <!> jreq "prism" (function Prism (x, y, z) -> Some (x, y, z) | _ -> None) )
160+
static member get_Codec () =
161+
(Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None))
162+
<|> (Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None))
163+
<|> (Prism <!> jreq "prism" (function Prism (x, y, z) -> Some (x, y, z) | _ -> None))
164+
|> ofObjCodec
179165
```
166+
167+
180168
or using the jchoice combinator:
181169
```fsharp
182170
type Shape with
@@ -187,24 +175,76 @@ type Shape with
187175
Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None)
188176
Prism <!> jreq "prism" (function Prism (x, y, z) -> Some (x, y, z) | _ -> None)
189177
]
178+
|> ofObjCodec
190179
191180
```
192181

182+
But codecs for both types can easily be written with the codec computation expressions
183+
184+
```fsharp
185+
186+
type Person = {
187+
name : string * string
188+
age : int option
189+
children: Person list }
190+
with
191+
static member get_Codec () =
192+
codec {
193+
let! f = jreq "firstName" (Some << fun x -> fst x.name)
194+
and! l = jreq "lastName" (Some << fun x -> snd x.name)
195+
and! a = jopt "age" (fun x -> x.age) // Optional fields can use 'jopt'
196+
and! c = jreq "children" (fun x -> Some x.children)
197+
return { name = (f, l); age = a; children = c } }
198+
|> ofObjCodec
199+
200+
201+
type Shape =
202+
| Rectangle of width : float * length : float
203+
| Circle of radius : float
204+
| Prism of width : float * float * height : float
205+
with
206+
static member get_Codec () =
207+
codec {
208+
Rectangle <!> jreq "rectangle" (function Rectangle (x, y) -> Some (x, y) | _ -> None)
209+
Circle <!> jreq "radius" (function Circle x -> Some x | _ -> None)
210+
Prism <!> jreq "prism" (function Prism (x, y, z) -> Some (x, y, z) | _ -> None)
211+
}
212+
|> ofObjCodec
213+
214+
```
215+
216+
193217
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.
194218

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

197221
```fsharp
198222
open System.Text
223+
open Fleece.SystemTextJson.Operators
224+
225+
type Person = {
226+
name : string * string
227+
age : int option
228+
children: Person list }
229+
with
230+
static member JsonObjCodec: Codec<PropertyList<Fleece.SystemTextJson.Encoding>, Person> = codec {
231+
let! f = jreq "firstName" (Some << fun x -> fst x.name)
232+
and! l = jreq "lastName" (Some << fun x -> snd x.name)
233+
and! a = jopt "age" (fun x -> x.age) // Optional fields can use 'jopt'
234+
and! c = jreq "children" (fun x -> Some x.children)
235+
return { name = (f, l); age = a; children = c } }
199236
200237
let personBytesCodec =
201238
Person.JsonObjCodec
202239
|> Codec.compose jsonObjToValueCodec // this is the codec that fills the gap to/from JsonValue
203240
|> Codec.compose jsonValueToTextCodec // this is a codec between JsonValue and JsonText
204-
|> Codec.invmap Encoding.UTF8.GetString Encoding.UTF8.GetBytes // This is a pair of of isomorphic functions
241+
|> Codec.invmap (Encoding.UTF8.GetString: byte [] -> string) Encoding.UTF8.GetBytes // This is a pair of of isomorphic functions
242+
243+
let p = { name = "John", "Smith"; age = Some 42; children = [] }
244+
205245
206246
let bytePerson = Codec.encode personBytesCodec p
207-
// val bytePerson : byte [] = [|123uy; 13uy; 10uy; 32uy; 32uy; ... |]
247+
// val bytePerson : byte [] = [|123uy; 34uy; 102uy; 105uy; 114uy; 115uy; ... |]
208248
let p' = Codec.decode personBytesCodec bytePerson
209249
```
210250

@@ -236,20 +276,20 @@ let colorEncoder = function
236276
| Blue -> JString "blue"
237277
| White -> JString "white"
238278
239-
let colorCodec = colorDecoder, colorEncoder
279+
let colorCodec () = colorDecoder <-> colorEncoder
240280
241-
let [<GeneralizableValue>]carCodec<'t> =
281+
let carCodec () =
242282
fun i c k -> { Id = i; Color = c; Kms = k }
243283
|> withFields
244-
|> jfieldWith JsonCodec.string "id" (fun x -> x.Id)
245-
|> jfieldWith colorCodec "color" (fun x -> x.Color)
246-
|> jfieldWith JsonCodec.int "kms" (fun x -> x.Kms)
247-
|> Codec.compose jsonObjToValueCodec
284+
|> jfieldWith Codecs.string "id" (fun x -> x.Id)
285+
|> jfieldWith (colorCodec ()) "color" (fun x -> x.Color)
286+
|> jfieldWith Codecs.int "kms" (fun x -> x.Kms)
287+
|> Codec.compose (Codecs.propList Codecs.id)
248288
249289
let car = { Id = "xyz"; Color = Red; Kms = 0 }
250290
251-
let jsonCar = Codec.encode carCodec car
252-
// val jsonCar : JsonValue = {"id": "xyz", "color": "red", "kms": 0}
291+
let jsonCar : Fleece.SystemTextJson.Encoding = Codec.encode (carCodec ()) car
292+
// val jsonCar: SystemTextJson.Encoding = {"id":"xyz","color":"red","kms":0}
253293
```
254294

255295
## Maintainer(s)

0 commit comments

Comments
 (0)