JSONX is an Erlang library for efficient decode and encode JSON, written in C.
C Erlang
Latest commit 9c95948 Dec 9, 2013 @iskra Merge pull request #13 from hairyhum/master
Dialyzer friendly errors

README.md

JSONX is an Erlang library for efficient JSON decoding and encoding, implemented in Erlang NIFs. Works with binaries as strings, arrays as lists and only knows how to decode UTF-8 (and ASCII).

JSONX IS VERY FAST!

Check out a benchmark si14/erl_json_test or davisp/erljson_bench and record encoding tests in /test/bench_encode_records.erl

INSTALLATION and DOCUMENTATION

  • cd jsonx
  • make
  • make doc
  • firefox doc/index.html&

JSONX can encode and decode Erlang records!

-module(record_example).
-compile(export_all).

-record(person,  {name, age, friends}).
-record(person2, {name, age, phone}).

encoder1() ->
    jsonx:encoder([{person,   record_info(fields, person)},
                   {person2,  record_info(fields, person2)} ]).

decoder1() ->
    jsonx:decoder([{person,   record_info(fields, person)},
                   {person2,  record_info(fields, person2)}]).

nonstrict_decoder1() ->
    jsonx:decoder([{person,   record_info(fields, person)},
                   {person2,  record_info(fields, person2)}],
		  [{format, proplist}]).
1> c(records_examples).
{ok,records_examples}

2>  rr(record_examples).
[person,person2]

3> BabaYaga = #person2{name = <<"BabaYaga">>, age = 118, phone = <<"666-66-66">>}.
#person2{name = <<"BabaYaga">>,age = 118,
         phone = <<"666-66-66">>}

4>  Vasya = #person{name = <<"Vasya">>, age = 18, friends = [BabaYaga]}.
#person{name = <<"Vasya">>,age = 18,
        friends = [#person2{name = <<"BabaYaga">>,age = 118,
                            phone = <<"666-66-66">>}]}

5> Encoder = record_examples:encoder1().
#Fun<jsonx.0.45888425>

6> Decoder = record_examples:decoder1().
#Fun<jsonx.1.21317315>

7> Json = Encoder(BabaYaga).
<<"{\"name\": \"BabaYaga\",\"age\": 118,\"phone\": \"666-66-66\"}">>

8> Decoder(Json).
#person2{name = <<"BabaYaga">>,age = 118,
         phone = <<"666-66-66">>}
9> Json2 = Encoder(Vasya).
<<"{\"name\": \"Vasya\",\"age\": 18,\"friends\": [{\"name\": \"BabaYaga\",\"age\": 118,\"phone\": \"666-66-66\"}]}">>

10> Decoder(Json2).
#person{name = <<"Vasya">>,age = 18,
        friends = [#person2{name = <<"BabaYaga">>,age = 118,
                            phone = <<"666-66-66">>}]}

11> Json3 = <<"[{\"name\": \"BabaYaga\",\"age\": 118,\"phone\": \"666-66-66\"}, {\"record\": \"undefined\", \"strict\": false}]">>.
<<"[{\"name\": \"BabaYaga\",\"age\": 118,\"phone\": \"666-66-66\"}, {\"record\": \"undefined\", \"strict\": false}]">>

12> Decoder(Json3).
{error,undefined_record,64}

13>  NonStrictDecoder = record_examples:nonstrict_decoder1().
#Fun<jsonx.2.71844966>

14> JTerm =  NonStrictDecoder(Json3).
[#person2{name = <<"BabaYaga">>,age = 118,
          phone = <<"666-66-66">>},
 [{<<"record">>,<<"undefined">>},{<<"strict">>,false}]]

15> Encoder(JTerm).
<<"[{\"name\": \"BabaYaga\",\"age\": 118,\"phone\": \"666-66-66\"},{\"record\":\"undefined\",\"strict\":false}]">>

Examples encoding JSON

1>  jsonx:encode([1, 2.3, true, false, null, atom, <<"string">>, []]).
<<"[1,2.3,true,false,null,\"atom\",\"string\",[]]">>

%% Object as proplist
2>  jsonx:encode( [{name, <<"Ivan">>}, {age, 33}, {phones, [3332211, 4443322]}] ).
<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>

%% Object as struct
3>  jsonx:encode( {struct, [{name, <<"Ivan">>}, {age, 33}, {phones, [3332211, 4443322]}]} ).
<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>

%% Object as eep18 propsal
4>  jsonx:encode( {[{name, <<"Ivan">>}, {age, 33}, {phones, [3332211, 4443322]}]} ).
<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>

Examples decoding JSON

1> jsonx:decode(<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>).
{[{<<"name">>,<<"Ivan">>},
  {<<"age">>,33},
  {<<"phones">>,[3332211,4443322]}]}

2> jsonx:decode(<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>, [{format, eep18}]).
{[{<<"name">>,<<"Ivan">>},
  {<<"age">>,33},
  {<<"phones">>,[3332211,4443322]}]}

3> jsonx:decode(<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>, [{format, proplist}]).
[{<<"name">>,<<"Ivan">>},
 {<<"age">>,33},
 {<<"phones">>,[3332211,4443322]}]

4> jsonx:decode(<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>, [{format, struct}]). 
{struct,[{<<"name">>,<<"Ivan">>},
         {<<"age">>,33},
         {<<"phones">>,[3332211,4443322]}]}

Example streaming parse

More example see examples/stream_example.erl .

1> D = jstream:new_decoder(<<"{\"key1\": \"val1\",\n">>).
<<>>

2> jstream:get_event(D).
start_map

3> jstream:get_event(D).
{map_key,<<"key1">>}

4> jstream:get_event(D).
<<"val1">>

5> jstream:get_event(D).
parse_buf

6> ok = jstream:update_decoder(D, <<"\"key2\": \"val2\"}\n">>).
ok

7> jstream:get_event(D).
{map_key,<<"key2">>}

8> jstream:get_event(D).
<<"val2">>

9> jstream:get_event(D).
end_map

10> jstream:get_event(D).
{parse_end,<<>>}

Mapping (JSON -> Erlang)

null             :-> null
true             :-> true
false            :-> false
"string"         :-> <<"binary">>
[1, 2.3, []]     :-> [1, 2.3, []]
{"this": "json"} :-> {[{<<"this">>: <<"json">>}]}         %% default eep18
{"this": "json"} :-> [{<<"this">>: <<"json">>}]           %% optional proplist
{"this": "json"} :-> {struct, [{<<"this">>: <<"json">>}]} %% optional struct
JSONObject       :-> #rec{...}                            %% decoder must be predefined

Mapping (Erlang -> JSON)

null                                 :-> null
true                                 :-> true
false                                :-> false
atom                                 :-> "atom"
<<"str">>                            :-> "str"
[1, 2.99]                            :-> [1, 2.99]
{struct, [{<<"this">>: <<"json">>}]} :-> {"this": "json"}
[{<<"this">>: <<"json">>}]           :-> {"this": "json"}
{[{<<"this">>: <<"json">>}]}         :-> {"this": "json"}
{json, IOList}                       :-> `iolist_to_binary(IOList)`  %% include with no validation
#rec{...}                            :-> JSONObject                  %% encoder must be predefined