opam-version: "2.0" synopsis: "Elm-inspired decoders for Ocaml" description: """ 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: 1. Get your data from somewhere. Now you have a `string`. 2. *Parse* the `string` as JSON (or YAML). Now you have a `Yojson.Basic.json`, or maybe an `Ezjsonm.value`, or perhaps a `Ocaml.yaml`. 3. *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. ```ocaml utop # module D = Decoders_yojson.Basic.Decode;; module D = Decoders_yojson.Basic.Decode ``` Let's set our sights high and decode an integer. ```ocaml 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. ```ocaml utop # D.decode_value;; - : 'a decoder -> value -> ('a, error) result = ``` For convenience we also have `decode_string`, which takes a `string` and calls `Yojson`'s parser under the hood. ```ocaml 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. ```ocaml 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? ```ocaml 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. ```ocaml 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. ```ocaml 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. ```ocaml 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: ```ocaml 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 dune-release tag dune-release -p decoders,decoders-ezjsonm,decoders-yojson ```""" maintainer: "Matt Bray " authors: "Matt Bray " license: "ISC" homepage: "https://github.com/mattjbray/ocaml-decoders" doc: "https://mattjbray.github.io/ocaml-decoders/decoders" bug-reports: "https://github.com/mattjbray/ocaml-decoders/issues" depends: [ "ocaml" "jbuilder" {>= "1.0+beta9"} "cppo" {build} "containers" {>= "2.8"} ] build: ["jbuilder" "build" "-p" name "-j" jobs] conflicts: [ "dune" {>="1.7.0"} ] dev-repo: "git+ssh://git@github.com/mattjbray/ocaml-decoders.git" url { src: "https://github.com/mattjbray/ocaml-decoders/releases/download/0.1.0/decoders-0.1.0.tbz" checksum: [ "sha256=291e46eab27e1c7d14b9dca366e37a0b54817ab1801dd60a565e7270fd3abb0a" "md5=a97f370f3f7ff68d085ac01e05fab493" ] }