Skip to content

odo/jsonx

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

75 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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).

Map encoding/decoding supported in Erlang/OTP 17+

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

Some quick benchmark against jiffy (the fastest lib I knew so far):

decoding with jiffy 2241 ms
decoding with jsonx 1146 ms
encoding with jiffy 1735 ms
encoding with jsonx 519 ms

This was tested with 10 000 json files of varying lenghts and a total size of 41MB.

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, nil, 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]}">>

%% Object as Map
5>  jsonx:encode( #{name => <<"Ivan">>, age => 33, phones => [3332211, 4443322]} ).
<<"{\"age\":33,\"name\":\"Ivan\",\"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]}]}

5> jsonx:decode(<<"{\"name\":\"Ivan\",\"age\":33,\"phones\":[3332211,4443322]}">>, [{format, map}]).
#{<<"age">> => 33,<<"name">> => <<"Ivan">>,
  <<"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             :-> nil
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
{"this": "json"} :-> #{<<"this">> => <<"json">>}          %% optional map
JSONObject       :-> #rec{...}                            %% decoder must be predefined

Mapping (Erlang -> JSON)

nil                                  :-> 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"}
{[{<<"this">>: <<"json">>}]}         :-> {"this": "json"}
{json, IOList}                       :-> `iolist_to_binary(IOList)`  %% include with no validation
#rec{...}                            :-> JSONObject                  %% encoder must be predefined

About

JSONX is an Erlang library for efficient decode and encode JSON, written in C.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 55.9%
  • Erlang 32.7%
  • C++ 11.1%
  • Makefile 0.3%