git.iko.soy/rowtype-yoga/purescript-yoga-json
on main help sign in
Restructure readme layout (#14)

* Restructure readme layout * Add tutorial section

commitab7a5cb5c8026b012408a5906cd104489d11fce8 authorsigma-andex <77549848+sigma-andex@users.noreply.github.com> dateSat 19 Nov 2022, 09:13 (4 years ago) parent0a674d1
2 files changed+177−35
README.md+130−35 docs/simple-json-migration.md+47
README.md+130−35
@@ -1,67 +1,162 @@
11# purescript-yoga-json
22
3−**Note**: This is a fork of [simple-json](https://github.com/justinwoo/purescript-simple-json) ([MIT Licence](./LICENSE/simple-json.LICENSE)).
3+`yoga-json` is a light-weight and simple json library for purescript.
4+
5+**Note**: This library was initially forked from the amazing [simple-json](https://github.com/justinwoo/purescript-simple-json) ([MIT Licence](./LICENSE/simple-json.LICENSE)). See [migrate from `simple-json`](#migrate-from-purescript-simple-json) for migration instructions.
46
57## Table of Contents
8+* [features](#features)
9+* [installation](#installation)
610* [usage](#usage)
7−* [migrate from `simple-json`](#migrate-from-purescript-simple-json)
11+
12+## Features
13+
14+* 😌 simple and easy to use json codecs
15+* 🪶 light-weight
16+* 🤖 built-in support for many common types (`Sum` types, Tuples, BigInts, Maps, JSDate, DateTime, Eithers, NonEmptyStrings )
17+* 🖍 human-friendly error reporting
18+
19+## Installation
20+
21+```
22+spago install yoga-json
23+```
824
925## Usage
1026
27+`purescript-yoga-json` basically provides two functions `writeJSON` and `readJSON`.
28+
29+Use `writeJSON` to serialise a type to a JSON string:
30+
1131```purescript
1232import Yoga.JSON as JSON
1333
1434serialised :: String
1535serialised =
1636 JSON.writeJSON { first_name: "Lola", last_name: "Flores" }
37+ -- {"last_name":"Flores","first_name":"Lola"}
38+```
39+
40+Use `readJSON` to deserialise a JSON string:
41+```purescript
42+import Yoga.JSON as JSON
43+
44+deserialised :: Either MultipleErrors { first_name :: String, last_name :: String }
45+deserialised = JSON.readJSON """{ "first_name": "Lola", "last_name": "Flores" }"""
46+-- Right { first_name: "Lola", last_name: "Flores" }
47+```
48+
49+As the parsing can fail, `readJSON` returns an `Either`, potentially containing a `Left` data constructor with `MultipleErrors`. If you don't care about the specific errors, you can use `readJSON_`, which returns a `Maybe`:
50+
51+```purescript
52+deserialised :: Maybe { first_name :: String, last_name :: String }
53+deserialised = JSON.readJSON_ """{ "first_name": "Lola", "last_name": "Flores" }"""
54+-- Just { first_name: "Lola", last_name: "Flores" }
55+```
56+
57+
58+## Sum types
59+
60+`purescript-yoga-json` provides utility functions to easily generate serialisers and deserialisers for your sum types.
61+
62+Let's start with a simple example of a sum type where our data constructors do not contain any further values:
63+
1764```
65+import Data.Either (Either)
66+import Data.Generic.Rep (class Generic)
67+import Yoga.JSON as JSON
68+import Yoga.JSON.Generics (genericReadForeignEnum, genericWriteForeignEnum)
69+import Yoga.JSON.Generics.EnumSumRep as Enum
1870
19−Check out the tests for how to encode/decode increasingly complex types.
71+data MyEnum = Enum1 | Enum2 | Enum3
72+```
2073
21−## Migrate from `purescript-simple-json`
74+Now, we need to derive a `Generic` instance for it:
2275
23−`purescript-yoga-json` is almost (read below if you use variants) a drop-in replacement for `purescript-simple-json`. Just change the imports from `Simple.JSON` to `Yoga.JSON`.
76+```
77+derive instance Generic MyEnum _
78+-- and optionally a Show instance
79+instance Show MyEnum where
80+ show = genericShow
81+```
2482
25−## Additions over `simple-json`
83+Next, we define a `WriteForeign` instance and implement the `writeImpl` function using `genericWriteForeignEnum Enum.defaultOptions`.
2684
27−### Errors
28−yoga-json will actually report multiple errors at once in the case of records, objects, and arrays.
29−There's also a way to make errors more readable with `renderHumanError`:
30−(e.g. `Must provide a value of type 'Int' instead of 'Object' at $.a.b.c.d[1]`)
85+```purescript
86+instance WriteForeign MyEnum
87+ where
88+ writeImpl = genericWriteForeignEnum Enum.defaultOptions
89+```
3190
91+Similarly, we implement the `ReadForeign` instance using `genericReadForeignEnum Enum.defaultOptions`
92+```purescript
93+instance ReadForeign MyEnum
94+ where
95+ readImpl = genericReadForeignEnum Enum.defaultOptions
96+```
3297
33−### Tuples
34−There is an inbuilt codec for `Tuple`s thanks to @ursi
35−`yoga-json` represents tuples as arrays in JSON.
98+That's all, we can now serialise our data type:
99+```purescript
100+serialised = writeJSON { "myEnum": Enum3 }
101+-- {"myEnum":"Enum3"}
102+```
103+and deserialise it:
104+```purescript
105+deserialised :: E { "myEnum" :: MyEnum }
106+deserialised = readJSON serialised
107+-- Right { myEnum: Enum3 }
108+```
36109
37−### Eithers
38−There is an inbuilt codec for `Either`s.
39−`yoga-json` represents eithers as objects with a `type` and a `value` tag in JSON.
110+### Writing custom codecs
40111
41−### JSDate and DateTime
42−Both are encoded as ISO8601 strings as spat out by `JSDate.toISOString`.
112+In order to write your own, custom codecs you will need to provide instances for `WriteForeign` and `ReadForeign`.
43113
44−### Generics
45−It includes @justinwoo's codecs for en- and decoding generics inspired by
46−[simple-json-generics](https://github.com/justinwoo/purescript-simple-json-generics)
114+Let's see an example. We define a simple data type `TrafficLight` that has three data constructors `Red`, `Yellow` and `Green`:
115+```purescript
116+data TrafficLight = Red | Yellow | Green
117+```
118+We would like to serialise these constructors as lower case strings `"red"`, `"yellow"` and `"green"`.
47119
48−It is possible to customise the representation of enums, tagged sum types, and untagged sum types via options.
120+First we need to provide an implementation for the `WriteForeign` type class, that tells `yoga-json` how to serialise the type. We pattern match on the three different data constructors and write them as the three `String`s red, yellow and green, using the same `writeImpl` function:
121+```purescript
122+instance WriteForeign TrafficLight where
123+ writeImpl Red = writeImpl "red"
124+ writeImpl Yellow = writeImpl "yellow"
125+ writeImpl Green = writeImpl "green"
126+```
127+This works, because `yoga-json` already nows how to serialise a `String`.
49128
50−### BigInts
51−There is an inbuilt codec for `BigInt`. Writing a `BigInt` however will result in a String. This is a current limitation of JavaScript and the only way around it - it seems - is writing your own JSON.stringify method (which is out of scope of this project).
129+Similarly, we need to provide an implementation for the `ReadForeign` type class, that tells `yoga-json` how to deserialise the type. We start by deserialising into a primitive type, typically a String or an Object, and then convert it to our desired Purescript type.
52130
53−## Differences to `simple-json`
131+Since we don't know the input `String`, it might contain invalid data and therefore deserialisation might fail. `yoga-json` uses the `ExceptT` monad to reflect this. We can return valid values using `pure` and `fail` on invalid values:
54132
55−### 💣 Variant codec
56−If you want to emulate `simple-json`'s format you may use the newtype `TaggedVariant`
133+```purescript
134+instance ReadForeign TrafficLight where
135+ readImpl json = do
136+ -- deserialise into a string
137+ str :: String <- readImpl json
138+ -- now we pattern match on our valid types
139+ case str of
140+ "red" -> pure Red
141+ "yellow" -> pure Yellow
142+ "green" -> pure Green
143+ -- and fail if we get an invalid type
144+ other -> fail $ ForeignError $ "Failed to parse " <> other <> " as TrafficLight"
145+```
57146
147+Now we can serialise our data type:
58148```purescript
59−type YourVariantRow = ( a :: Int, b :: String )
60−type YourVariant = Variant YourVariantRow
61−x :: YourVariant
62−x = inj (Proxy :: Proxy "a") 5
63−-- encoded = writeJSON x
64−-- ^ Let's say you had this before
65−-- You can now do:
66−encoded = writeJSON (TaggedVariant "type" "value" x)
149+serialised = writeJSON { "trafficLight": Red }
150+-- {"trafficLight":"red"}
151+```
152+
153+and deserialise it:
154+```purescript
155+deserialised :: E { "trafficLight" :: TrafficLight }
156+deserialised = readJSON """{ "trafficLight": "green" }"""
157+-- Right { trafficLight: Green }
158+
159+deserialisedUnknown :: E { "trafficLight" :: TrafficLight }
160+deserialisedUnknown = readJSON """{ "trafficLight": "purple" }"""
161+-- Left (NonEmptyList (NonEmpty (ErrorAtProperty "trafficLight" (ForeignError "Failed to parse purple as TrafficLight")) Nil))
67162```
docs/simple-json-migration.mdnew file+47
@@ -0,0 +1,47 @@
1+# Migrate from `purescript-simple-json`
2+
3+`purescript-yoga-json` is a fork of [simple-json](https://github.com/justinwoo/purescript-simple-json) ([MIT Licence](./LICENSE/simple-json.LICENSE)) and almost (read below if you use variants) a drop-in replacement for `purescript-simple-json`. Just change the imports from `Simple.JSON` to `Yoga.JSON`.
4+
5+## Additions over `simple-json`
6+
7+### Errors
8+yoga-json will actually report multiple errors at once in the case of records, objects, and arrays.
9+There's also a way to make errors more readable with `renderHumanError`:
10+(e.g. `Must provide a value of type 'Int' instead of 'Object' at $.a.b.c.d[1]`)
11+
12+
13+### Tuples
14+There is an inbuilt codec for `Tuple`s thanks to @ursi
15+`yoga-json` represents tuples as arrays in JSON.
16+
17+### Eithers
18+There is an inbuilt codec for `Either`s.
19+`yoga-json` represents eithers as objects with a `type` and a `value` tag in JSON.
20+
21+### JSDate and DateTime
22+Both are encoded as ISO8601 strings as spat out by `JSDate.toISOString`.
23+
24+### Generics
25+It includes @justinwoo's codecs for en- and decoding generics inspired by
26+[simple-json-generics](https://github.com/justinwoo/purescript-simple-json-generics)
27+
28+It is possible to customise the representation of enums, tagged sum types, and untagged sum types via options.
29+
30+### BigInts
31+There is an inbuilt codec for `BigInt`. Writing a `BigInt` however will result in a String. This is a current limitation of JavaScript and the only way around it - it seems - is writing your own JSON.stringify method (which is out of scope of this project).
32+
33+## Differences to `simple-json`
34+
35+### 💣 Variant codec
36+If you want to emulate `simple-json`'s format you may use the newtype `TaggedVariant`
37+
38+```purescript
39+type YourVariantRow = ( a :: Int, b :: String )
40+type YourVariant = Variant YourVariantRow
41+x :: YourVariant
42+x = inj (Proxy :: Proxy "a") 5
43+-- encoded = writeJSON x
44+-- ^ Let's say you had this before
45+-- You can now do:
46+encoded = writeJSON (TaggedVariant "type" "value" x)
47+```