decoders-yojsonversion
Elm-inspired decoders for Ocaml
A combinator library for "decoding" JSON-like values into your own Ocaml types, inspired by Elm's Json.Decode
and Json.Encode
.
Eh?
An Ocaml program having a JSON (or YAML) data source usually goes something like this:
- Get your data from somewhere. Now you have a
string
. - Parse the
string
as JSON (or YAML). Now you have aYojson.Basic.json
, or maybe anEzjsonm.value
, or perhaps aOcaml.yaml
. - Decode the JSON value to an Ocaml type that's actually useful for your program's domain.
This library helps with step 3.
Getting started
Install one of the supported decoder backends:
opam install decoders-ezjsonm # For Ezjsonm
opam install decoders-ocyaml # For Ocyaml
opam install decoders-yojson # For Yojson
npm install --save-dev bs-decoders # For Bucklescript
Now we can start decoding stuff!
First, a module alias to save some keystrokes. In this guide, we'll parse JSON
using Yojson
's Basic
variant.
utop # module D = Decoders_yojson.Basic.Decode;;
module D = Decoders_yojson.Basic.Decode
Let's set our sights high and decode an integer.
utop # D.decode_value D.int (`Int 1);;
- : (int, error) result = Ok 1
Nice! We used decode_value
, which takes a decoder
and a value
(in this
case a Yojson.Basic.json
) and... decodes the value.
utop # D.decode_value;;
- : 'a decoder -> value -> ('a, error) result = <fun>
For convenience we also have decode_string
, which takes a string
and calls
Yojson
's parser under the hood.
utop # D.decode_string D.int "1";;
- : (int, error) result = Ok 1
What about a list
of int
s? Here's where the "combinator" part comes in.
utop # D.decode_string D.(list int) "[1,2,3]";;
- : (int list, error) result = Ok [1; 2; 3]
Success!
Ok, so what if we get some unexpected JSON?
utop # #install_printer D.pp_error;;
utop # D.decode_string D.(list int) "[1,2,true]";;
- : (int list, error) result =
Error while decoding a list: element 2: Expected an int, but got true
Generic decoders
Suppose our program deals with users and roles. We want to decode our JSON input into these types.
type role = Admin | User
type user =
{ name : string
; roles : role list
}
Let's define our decoders. We'll write a module functor so we can re-use the same decoders across different JSON libraries, with YAML input, or with Bucklescript.
module My_decoders(D : Decoders.Decode.S) = struct
open D
let role : role decoder =
string >>= function
| "ADMIN" -> succeed Admin
| "USER" -> succeed User
| _ -> fail "Expected a role"
let user : user decoder =
field "name" string >>= fun name ->
field "roles" (list role) >>= fun roles ->
succeed { name; roles }
end
module My_yojson_decoders = My_decoders(Decoders_yojson.Basic.Decode)
Great! Let's try them out.
utop # open My_yojson_decoders;;
utop # D.decode_string role {| "USER" |};;
- : (role, error) result = Ok User
utop # D.decode_string D.(field "users" (list user))
{| {"users": [{"name": "Alice", "roles": ["ADMIN", "USER"]},
{"name": "Bob", "roles": ["USER"]}]}
|};;
- : (user list, error) result =
Ok [{name = "Alice"; roles = [Admin; User]}; {name = "Bob"; roles = [User]}]
Let's introduce an error in the JSON:
utop # D.decode_string D.(field "users" (list user))
{| {"users": [{"name": "Alice", "roles": ["ADMIN", "USER"]},
{"name": "Bob", "roles": ["SUPER_USER"]}]}
|};;
- : (user list, error) result =
Error
in field "users":
while decoding a list:
element 1:
in field "roles":
while decoding a list:
element 0: Expected a role, but got "SUPER_USER"
We get a nice pointer that we forgot to handle the SUPER_USER
role.
Release
After updating CHANGES.md:
npm version <newversion>
dune-release tag
dune-release -p decoders,decoders-ezjsonm,decoders-yojson
Author | Matt Bray <matt@aestheticintegration.com> |
---|---|
License | ISC |
Published | |
Homepage | https://github.com/mattjbray/ocaml-decoders |
Issue Tracker | https://github.com/mattjbray/ocaml-decoders/issues |
Maintainer | Matt Bray <matt@aestheticintegration.com> |
Dependencies | |
Source [http] | https://github.com/mattjbray/ocaml-decoders/releases/download/0.1.0/decoders-0.1.0.tbz sha256=291e46eab27e1c7d14b9dca366e37a0b54817ab1801dd60a565e7270fd3abb0a md5=a97f370f3f7ff68d085ac01e05fab493 |
Edit | https://github.com/ocaml/opam-repository/tree/master/packages/decoders-yojson/decoders-yojson.0.1.0/opam |
- eriswith-test
- eris-lwtwith-test
- ppx_deriving_decoderswith-test