• Stars
    star
    275
  • Rank 149,796 (Top 3 %)
  • Language
    OCaml
  • License
    GNU Lesser Genera...
  • Created over 7 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Compositional JSON encode/decode library for BuckleScript

bs-json

Compositional JSON encode/decode library for BuckleScript.

NOTE: For rescript users, please see the spiritual successor library rescript-json-combinators

npm Travis Coveralls Issues Last Commit

The Decode module in particular provides a basic set of decoder functions to be composed into more complex decoders. A decoder is a function that takes a Js.Json.t and either returns a value of the desired type if successful or raises a DecodeError exception if not. Other functions accept a decoder and produce another decoder. Like array, which when given a decoder for type t will return a decoder that tries to produce a value of type t array. So to decode an int array you combine Json.Decode.int with Json.Decode.array into Json.Decode.(array int). An array of arrays of ints? Json.Decode.(array (array int)). Dict containing arrays of ints? Json.Decode.(dict (array int)).

Example

type line = {
  start: point,
  end_: point,
  thickness: option(int)
}
and point = {
  x: int,
  y: int
};

module Decode = {
  let point = json =>
    Json.Decode.{
      x: json |> field("x", int),
      y: json |> field("y", int)
    };

  let line = json =>
    Json.Decode.{
      start:     json |> field("start", point),
      end_:      json |> field("end", point),
      thickness: json |> optional(field("thickness", int))
    };
};

let data = {| {
  "start": { "x": 1, "y": -4 },
  "end":   { "x": 5, "y": 8 }
} |};

let line = data |> Json.parseOrRaise
                |> Decode.line;

NOTE: Json.Decode.{ ... } creates an ordinary record, but also opens the Json.Decode module locally, within the scope delimited by the curly braces, so we don't have to qualify the functions we use from it, like field, int and optional here. You can also use Json.Decode.( ... ) to open the module locally within the parentheses, if you're not creating a record.

See examples for more.

Installation

npm install --save @glennsl/bs-json

Then add @glennsl/bs-json to bs-dependencies in your bsconfig.json:

{
  ...
  "bs-dependencies": ["@glennsl/bs-json"]
}

Documentation

API

For the moment, please see the interface files:

Writing custom decoders and encoders

If you look at the type signature of Json.Decode.array, for example, you'll see it takes an 'a decoder and returns an 'a array decoder. 'a decoder is just an alias for Js.Json.t -> 'a, so if we expand the type signature of array we'll get (Js.Json.t -> 'a) -> Js.Json.t -> 'a array. We can now see that it is a function that takes a decoder and returns a function, itself a decoder. Applying the int decoder to array will give us an int array decoder, a function Js.Json.t -> int array.

If you've written a function that takes just Js.Json.t and returns user-defined types of your own, you've already been writing composable decoders! Let's look at Decode.point from the example above:

let point = json => {
  open! Json.Decode;
  {
    x: json |> field("x", int),
    y: json |> field("y", int)
  };
};

This is a function Js.Json.t -> point, or a point decoder. So if we'd like to decode an array of points, we can just pass it to Json.Decode.array to get a point array decoder in return.

Builders

To write a decoder builder like Json.Decode.array we need to take another decoder as an argument, and thanks to currying we just need to apply it where we'd otherwise use a fixed decoder. Say we want to be able to decode both int points and float points. First we'd have to parameterize the type:

type point('a) = {
  x: 'a,
  y: 'a
}

Then we can change our point function from above to take and use a decoder argument:

let point = (decodeNumber, json) => {
  open! Json.Decode;
  {
    x: json |> field("x", decodeNumber),
    y: json |> field("y", decodeNumber)
  };
};

And if we wish we can now create aliases for each variant:

let intPoint = point(Json.Decode.int);
let floatPoint = point(Json.Decode.float);

Encoders

Encoders work exactly the same way, just in reverse. 'a encoder is just an alias for 'a -> Js.Json.t, and this also transfers to composition: 'a encoder -> 'a array encoder expands to ('a -> Js.Json.t) -> 'a array -> Js.Json.t.

License

This work is dual-licensed under LGPL 3.0 and MPL 2.0. You can choose between one of them if you use this work.

Please see LICENSE.LGPL-3.0 and LICENSE.MPL-2.0 for the full text of each license.

SPDX-License-Identifier: LGPL-3.0 OR MPL-2.0

Changes

5.0.4

  • Rewrote Encode.list to be stack-safe and much faster.

5.0.2

  • Added Json.Decode.id

5.0.1

  • Dual licensed as LGPL-3.0 and MPL-2.0. MPL is mostly equivalent to LGPL but relaxes its restriction on linking, which works better with the JavaScript packaging and distribution model.

5.0.0

  • Removed deprecated arrayOf encoder
  • Renamed dict encoder to jsonDict
  • Added new dict encoder that takes an additional encoder argument used to encode the contained values, and so it's consistent with the respective dict decoder.

4.0.0

  • Bumped bs-platform peer dependency to 5.0.4 to stop the compiler's complaining.

3.0.0

  • Replace usage of Js.Date.toJSON with Js.Date.toJSONUsafe, which is exactly the same, just to avoid deprecation warnings for end users (Thanks Bob!)
  • Requires bs-platform >= 4.0.2

2.0.0

  • Removed Json.Decode.boolean, Json.Encode.boolean, Json.Encode.booleanArray
  • Requires bs-platform >= 3.0.0

1.3.1

  • Reverted commits that broke backwards compatibility despite only affecting the implementation

1.3.0

  • Deprecated Json.Decode.boolean, Json.Encode.boolean, Json.Encode.booleanArray
  • Added Json.Encode.boolArray

1.2.0

  • Added Json.Encode.char and Json.Decode.char

1.1.0

  • Added "stack traces" to higher-order decoders, making it easier to find the location of an error.

1.0.1

  • Moved repository from reasonml-community/bs-json to glennsl/bs-json
  • Renamed NPM package from bs-json to @glennsl/bs-json

1.0.0

  • Replaced Json.Encoder.array with Json.Encode.arrayOf renamed to array. Deprecated arrayOf alias.
  • Added Json.parse, Json.parseOrRaise, Json.stringify
  • Added date encoder and decoder
  • Added tuple2/tuple3/tuple4 encoders and decoders
  • Fixed bug where js integers > 32-bit were rejected as integers by Json.Decode.int (#15)

0.2.4

  • Added Json.Encode.bool
  • Added Json.Encode.pair
  • Added Json.Encode.withDefault
  • Added Json.Encode.nullable
  • Added Json.Encode.arrayOf
  • Added Json.Encode.jsonArray as replacement for Json.Encode.array
  • Deprecated Json.Encode.array

0.2.3

  • Fixed embarrassing bug where an API was used that isn't available on IE (honestly more embarrassed on behalf of IE though)

0.2.2

  • Added Json.Decode.pair

0.2.1

  • Added Json.Encode.list

0.2.0

  • Breaking: Renamed Json.Encode.object_ to Json.Encode.dict
  • Added Json.Encode.object_ taking a list of properties instead of a Json.Dict.t as before

More Repositories

1

bucklescript-cookbook

A collection of simple examples showcasing the BuckleScript ecosystem
395
star
2

rescript-jest

ReScript bindings for Jest
ReScript
291
star
3

bucklescript-ffi-cheatsheet

165
star
4

rescript-fetch

Zero-cost rescript bindings to the WHATWG Fetch API
ReScript
53
star
5

rebase

A minimal base library for Reason
JavaScript
51
star
6

rescript-json-combinators

Combinator library for JSON decoding and encoding.
ReScript
45
star
7

vrroom

A collection of mostly experimental tools and utilities for effective ReasonReact development.
OCaml
28
star
8

refetch

A strongly typed and immutable API built on top of the `fetch` standard
JavaScript
24
star
9

reason-react-quick-start

[OBSOLETE] Quick start guide for Reason-React
24
star
10

timber

Opinionated logging framework based on @dbuenzli/logs
Reason
20
star
11

kakode

Kakoune mode for Visual Studio Code
OCaml
18
star
12

bs-typed-css

CSS. Typed.
JavaScript
15
star
13

bs-revamp

A safe and functional API for JavaScript regexes
JavaScript
12
star
14

sane-reason-grammar

11
star
15

bs-in-a-box

JavaScript
9
star
16

bs-typed-glamor

Typed interface to Glamor based on bs-typed-css-core
JavaScript
9
star
17

realm

OCaml
9
star
18

rebug

A port of debug to BuckleScript
JavaScript
9
star
19

rust-re

Regular expression enigne in Rust
Rust
9
star
20

reasonable-gatsby-starter

CSS
9
star
21

bs-refmt

OCaml
8
star
22

infinite-jest

Experimental cross-platform native/BuckleScript test framework
OCaml
6
star
23

checkmarked

Extracts code blocks from markdown files, then runs checks based on language
JavaScript
6
star
24

rescript-geojson

Simple GeoJSON decoder library for ReScript based on @glennsl/rescript-json-combinators.
ReScript
6
star
25

quickiecheck

Experimental BuckleScript quickcheck implementation
JavaScript
5
star
26

reason-syntax-guide

4
star
27

bs-typed-css-core

OCaml
3
star
28

bs-benchmarkjs

Bindings to Benchmark.js
OCaml
3
star
29

ahrefs-country-selector

ReScript
2
star
30

seed-calendar

Rust
2
star
31

bs-jsx-rewriter

OCaml
2
star
32

bs-logo

2
star
33

bs-vscode

Visual Studio Code extension API bindings for BuckleScript
2
star
34

rescratch

JavaScript
2
star
35

leafy-example

Rust
1
star
36

rescript-react-hooks

Better hooks for rescript-react.
ReScript
1
star
37

slides

1
star
38

reasonable-atom-starter

JavaScript
1
star
39

bs-atom

Atom bindings for BuckleScript - Deprecated in favor of https://github.com/banacorn/bs-atom
OCaml
1
star
40

vscode-duotone-dark-faithful

JavaScript
1
star
41

jsoo-react-external-children-repro

Makefile
1
star