Working with JSON
Std.Json turns Polar values into JSON text and back.
Decoding checks the shape of the data, so a decoded value really has
the type you asked for.
Make a type encodable
A type can be encoded once it has the Json trait. For
your own types, derive it:
uses
Std.Json
Std.List
Std.Option
Std.Result
types
Priority = Low | High derive(Eq, Json)
Note = {
id: Int,
text: String,
priority: Priority,
tags: List<String>,
due: Option<String>,
} derive(Json)
Every field type needs Json too. The basic types,
List, Option and
Result already have it, and
Priority derives it.
Encode
Json.encode turns a value into a string:
let note: Note = {
id: 1,
text: "Buy milk",
priority: High,
tags: ["home"],
due: None,
}
Log.info(Json.encode(note))
{"id":1,"text":"Buy milk","priority":{"$":"High"},"tags":["home"],"due":null}
Records become objects, lists become arrays, and
None becomes null. Variant cases become an
object with a "$" key naming the case.
Note: The
let note: Note annotation matters here. Without it,
the literal is a plain record, not a Note, and has no
Json implementation. See
Records.
Decode
Json.decode goes the other way. Decoding can fail, so
it returns a Result. Tell Polar which type you expect
by annotating the result:
let good: Result<JsonError, Note> = Json.decode(
"{\"id\": 2, \"text\": \"Call Bob\", \"priority\": {\"$\": \"Low\"}, \"tags\": [], \"due\": \"friday\"}",
)
match good {
Ok(n) -> Log.info("decoded #{n.text}, due #{Option.with_default(n.due, "whenever")}"),
Err(e) -> Log.info(Json.message(e)),
}
decoded Call Bob, due friday
Decode errors
When the data doesn't fit the type, you get a
JsonError saying where and why.
Json.message turns it into readable text:
let bad: Result<JsonError, Note> = Json.decode("{\"id\": \"two\"}")
.id: expected Int, found a string
A JsonError is a record with path,
expected and found fields, if you want to
build your own message.
Tip: Decode at the edges of your program, where data arrives from a request, a file or another service. After that, the rest of your code works with typed values and never needs to check them again.
Where else JSON is used
Bridges send values between browser and
server as JSON, so the types they carry need Json. The
example web framework's Response.json uses it to build
API responses.