glazejson

View Source

build Hex.pm Hex.pm

Fast Erlang NIF JSON encoder/decoder backed by the glaze C++ library, with a hand-rolled recursive-descent decoder and direct term-to-JSON encoder that produce/consume native Erlang terms in a single pass.

Features

  • Decoding straight to Erlang terms: maps, lists, binaries, integers (including bignums), floats, booleans, and null
  • Encoding Erlang terms straight to JSON, including big integers
  • Configurable representation of JSON null and JSON object keys
  • minify/1 and prettify/1 helpers
  • Standalone big-integer encode/decode helpers (encode_bigint/1, decode_bigint/1)

Installation

Add glazejson to your rebar.config deps:

{deps, [glazejson]}.

Building the NIF requires a C++23 compiler (GCC 12+ or Clang 16+) and CMake; the glaze C++ library is fetched automatically at build time via CMake's FetchContent. The top-level Makefile wires the CMake build into rebar3 compile, so a plain

rebar3 compile

is enough — it builds priv/glazejson.so and compiles the Erlang sources.

Usage

1> glazejson:decode(<<"{\"a\":1,\"b\":[true,null,3.5]}">>).
#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}

2> glazejson:encode(#{<<"a">> => 1, <<"b">> => [true, null, 3.5]}).
<<"{\"a\":1,\"b\":[true,null,3.5]}">>

3> glazejson:encode(#{a => 1}, [pretty]).
<<"{\n  \"a\": 1\n}">>

4> glazejson:minify(<<" { \"a\" : 1 } ">>).
{ok, <<"{\"a\":1}">>}

5> glazejson:prettify(<<"{\"a\":1}">>).
{ok, <<"{\n  \"a\": 1\n}">>}

JSON null

By default, JSON null decodes to (and null encodes from) the atom null. This can be overridden:

  • Application-wide, via the null environment key — set this once in your sys.config (or rebar.config relx/shell config) and every call uses it as the default:

    {glazejson, [{null, nil}]}
  • Per call, with the use_nil shorthand or the {null_term, Atom} option (see Options below). Per-call options always take precedence over the application-wide default.

Big integers

JSON numbers that don't fit into a 64-bit integer are decoded as Erlang big integers (and big integers are encoded back to their exact decimal JSON representation):

1> glazejson:decode(<<"123456789012345678901234567890">>).
123456789012345678901234567890

2> glazejson:encode(123456789012345678901234567890).
<<"123456789012345678901234567890">>

encode_bigint/1 and decode_bigint/1 expose the same conversion routines directly, independent of JSON parsing/encoding:

1> glazejson:encode_bigint(123456789012345678901234567890).
{ok, <<"123456789012345678901234567890">>}

2> glazejson:decode_bigint(<<"123456789012345678901234567890">>).
{ok, 123456789012345678901234567890}

Options

Decode options (decode/2)

OptionDescription
return_mapsDecode JSON objects as Erlang maps (default)
object_as_tupleDecode JSON objects as {[{Key, Value}]} proplist tuples (jiffy-style)
use_nilUse the atom nil for JSON null
{null_term, Atom}Use Atom for JSON null
{keys, atom}Decode object keys as atoms (via binary_to_atom/2-equivalent)
{keys, existing_atom}Decode object keys as existing atoms, falling back to binaries for unknown atoms
{keys, binary}Decode object keys as binaries (default)
1> glazejson:decode(<<"{\"a\":1}">>, [object_as_tuple]).
{[{<<"a">>, 1}]}

2> glazejson:decode(<<"{\"a\":1}">>, [{keys, atom}]).
#{a => 1}

3> glazejson:decode(<<"null">>, [use_nil]).
nil

4> glazejson:decode(<<"null">>, [{null_term, undefined}]).
undefined

Encode options (encode/2)

OptionDescription
prettyPretty-print the JSON output with two-space indentation
uescapeEscape non-ASCII characters as \uXXXX sequences
force_utf8Sanitize invalid UTF-8 byte sequences before encoding
use_nilEncode the atom nil as JSON null
{null_term, Atom}Encode Atom as JSON null
1> glazejson:encode(#{a => 1}, [pretty]).
<<"{\n  \"a\": 1\n}">>

2> glazejson:encode(<<"héllo"/utf8>>, [uescape]).
<<"\"h\\u00e9llo\"">>

3> glazejson:encode(nil, [use_nil]).
<<"null">>

API

FunctionDescription
decode/1, decode/2Decode a JSON binary or iolist to an Erlang term
encode/1, encode/2Encode an Erlang term to a JSON binary
minify/1Remove unnecessary whitespace from a JSON document
prettify/1Pretty-print a JSON document with two-space indentation
encode_bigint/1Encode an integer to its JSON decimal-string representation
decode_bigint/1Decode a JSON number string to an Erlang integer

See the module's EDoc comments (src/glazejson.erl) for full type specs and details.

Benchmarks

A comparison benchmark against other JSON libraries (simdjsone, jiffy, jason, thoas, euneus, OTP's built-in json, and torque) is available via:

$ make bench
Running benchmarks...

(numbers in µs)
               twitter (616.7K)     twitter2 (758.0K)     openrtb (1.2K)         esad (1.3K)         small (0.1K)
               decode   encode     decode   encode     decode   encode     decode   encode     decode   encode
---------------------------------------------------------------------------------------------------------------------
glazejson     14160.9   4774.0    13771.9   7049.2       15.7     12.0       14.1      7.4        1.1      1.9
torque        15895.8   4613.0    12699.7   5822.6       15.4     11.6       16.6      7.1        3.6      2.2
simdjsone     15764.0   8182.0    17115.5  12931.4       25.7     27.3       15.4     20.1        2.0      4.4
jiffy         35805.8   4622.6    45737.4   8915.5       41.7     21.8       29.6     11.8        6.5      3.0
jason         27518.9  12206.7    37930.1  22581.0       56.1     28.7       29.5     20.8        4.1      5.1
thoas         27267.8  13443.9    38068.6  23706.7       47.5     36.5       35.4     21.3        6.0      3.1
euneus        26325.0  11579.8    28975.9  21150.8       48.2     25.1       21.9     14.3        8.8      2.7
json          25733.0  11468.9    28398.7  20347.3       41.4     31.7       37.6      9.9        6.6      2.3

(requires the bench/dev Mix dependencies — see mix.exs).

Testing

make test

runs the EUnit test suite via rebar3 eunit.

License

MIT License — see LICENSE for details.