first attempt at integrating json lib with jsx lib
This commit is contained in:
parent
4c378791f9
commit
3ea3eba7b3
7 changed files with 96 additions and 124 deletions
2
makefile
2
makefile
|
@ -16,4 +16,4 @@ clean:
|
|||
./priv/backends.escript clean
|
||||
|
||||
install: compile
|
||||
./rebar install
|
||||
./rebar -f install
|
|
@ -71,7 +71,7 @@ term_to_json(JSON) ->
|
|||
term_to_json(JSON, []).
|
||||
|
||||
term_to_json(JSON, Opts) ->
|
||||
json_encoder:term_to_json(JSON, Opts).
|
||||
jsx_encoder:term_to_json(JSON, Opts).
|
||||
|
||||
|
||||
-spec json_to_term(JSON::binary()) -> json().
|
||||
|
@ -81,7 +81,7 @@ json_to_term(JSON) ->
|
|||
json_to_term(JSON, []).
|
||||
|
||||
json_to_term(JSON, Opts) ->
|
||||
json_decoder:json_to_term(JSON, Opts).
|
||||
jsx_eep0018:json_to_term(JSON, Opts).
|
||||
|
||||
|
||||
-spec is_json(JSON::binary()) -> true | false.
|
||||
|
@ -91,7 +91,7 @@ is_json(JSON) ->
|
|||
is_json(JSON, []).
|
||||
|
||||
is_json(JSON, Opts) ->
|
||||
json_verify:is_json(JSON, Opts).
|
||||
jsx_verify:is_json(JSON, Opts).
|
||||
|
||||
|
||||
-spec format(JSON::binary()) -> binary() | iolist().
|
||||
|
@ -101,7 +101,7 @@ format(JSON) ->
|
|||
format(JSON, []).
|
||||
|
||||
format(JSON, Opts) ->
|
||||
json_pp:pp(JSON, Opts).
|
||||
jsx_format:format(JSON, Opts).
|
||||
|
||||
|
||||
%% ----------------------------------------------------------------------------
|
||||
|
|
|
@ -81,16 +81,8 @@ collect({event, Start, Next}, [Current, Key, Parent|Rest], Opts)
|
|||
when Start =:= end_object; Start =:= end_array ->
|
||||
collect(Next(), [[{Key, lists:reverse(Current)}] ++ Parent] ++ Rest, Opts);
|
||||
|
||||
%% end of json is emitted asap (at close of array/object), calling Next() until {incomplete, More}
|
||||
%% and then More(end_stream) ensures the tail of the json binary is clean (whitespace only)
|
||||
collect({event, end_json, Next}, [[Acc]], _Opts) ->
|
||||
case Next() of
|
||||
{incomplete, More} -> case More(end_stream) of
|
||||
ok -> Acc
|
||||
; _ -> erlang:error(badarg)
|
||||
end
|
||||
; _ -> erlang:error(badarg)
|
||||
end;
|
||||
collect({event, end_json, _Next}, [[Acc]], _Opts) ->
|
||||
Acc;
|
||||
|
||||
%% key can only be emitted inside of a json object, so just insert it directly into
|
||||
%% the head of the accumulator and deal with it when we receive it's paired value
|
||||
|
|
|
@ -39,7 +39,7 @@ term_to_json(List, Opts) ->
|
|||
; true -> erlang:error(badarg)
|
||||
end,
|
||||
Encoding = proplists:get_value(encoding, Opts, utf8),
|
||||
json:format(event_generator(term_to_events(List)), [{output_encoding, Encoding}] ++ Opts).
|
||||
jsx:format(event_generator(lists:reverse(term_to_events(List))), [{output_encoding, Encoding}] ++ Opts).
|
||||
|
||||
event_generator([]) ->
|
||||
fun() -> {event, end_json, fun() -> {incomplete, fun(end_stream) -> ok end} end} end;
|
||||
|
@ -48,7 +48,7 @@ event_generator([Next|Rest]) ->
|
|||
|
||||
|
||||
term_to_events([{}]) ->
|
||||
[start_object, end_object];
|
||||
[end_object, start_object];
|
||||
term_to_events([First|_] = List) when is_tuple(First) ->
|
||||
proplist_to_events(List, [start_object]);
|
||||
term_to_events(List) when is_list(List) ->
|
||||
|
@ -65,7 +65,7 @@ proplist_to_events([{Key, Term}|Rest], Acc) ->
|
|||
; true -> erlang:error(badarg)
|
||||
end;
|
||||
proplist_to_events([], Acc) ->
|
||||
lists:reverse([end_object] ++ Acc);
|
||||
[end_object] ++ Acc;
|
||||
proplist_to_events(_, _) ->
|
||||
erlang:throw(badarg).
|
||||
|
||||
|
@ -73,7 +73,7 @@ proplist_to_events(_, _) ->
|
|||
list_to_events([Term|Rest], Acc) ->
|
||||
list_to_events(Rest, term_to_event(Term) ++ Acc);
|
||||
list_to_events([], Acc) ->
|
||||
lists:reverse([end_array] ++ Acc).
|
||||
[end_array] ++ Acc.
|
||||
|
||||
|
||||
term_to_event(List) when is_list(List) ->
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
-module(jsx_format).
|
||||
-author("alisdairsullivan@yahoo.ca").
|
||||
|
||||
-export([pp/2]).
|
||||
-export([format/2]).
|
||||
|
||||
-include("./include/jsx_types.hrl").
|
||||
|
||||
|
@ -53,12 +53,17 @@
|
|||
|
||||
-spec format(JSON::binary(), Opts::format_opts()) -> binary() | iolist().
|
||||
|
||||
pp(F, Opts) when is_function(F) ->
|
||||
prettify(F(), [], parse_opts(Opts, #opts{}), 0, start);
|
||||
|
||||
pp(JSON, Opts) when is_binary(JSON) ->
|
||||
format(JSON, Opts) when is_binary(JSON) ->
|
||||
P = jsx:parser(extract_parser_opts(Opts)),
|
||||
prettify(P(JSON), [], parse_opts(Opts, #opts{}), 0, start).
|
||||
format(fun() -> P(JSON) end, Opts);
|
||||
|
||||
format(F, OptsList) when is_function(F) ->
|
||||
Opts = parse_opts(OptsList, #opts{}),
|
||||
{Continue, String} = format_something(F(), Opts, 0),
|
||||
case Continue() of
|
||||
{event, end_json, _} -> encode(String, Opts)
|
||||
; _ -> {error, badarg}
|
||||
end.
|
||||
|
||||
|
||||
parse_opts([{indent, Val}|Rest], Opts) ->
|
||||
|
@ -79,81 +84,58 @@ extract_parser_opts(Opts) ->
|
|||
[ {K, V} || {K, V} <- Opts, lists:member(K, [comments, encoding]) ].
|
||||
|
||||
|
||||
prettify({event, start_object, Next}, Acc, Opts, Level, start) ->
|
||||
prettify(Next(), [Acc, ?start_object], Opts, Level + 1, new);
|
||||
prettify({event, start_object, Next}, Acc, Opts, Level, _) ->
|
||||
prettify(Next(),
|
||||
[Acc, ?comma, space(Opts), indent(Opts, Level), ?start_object],
|
||||
Opts,
|
||||
Level + 1,
|
||||
new);
|
||||
format_something({event, start_object, Next}, Opts, Level) ->
|
||||
{Continue, Object} = format_object(Next(), [], Opts, Level + 1),
|
||||
{Continue, [?start_object, Object, ?end_object]};
|
||||
format_something({event, start_array, Next}, Opts, Level) ->
|
||||
{Continue, Array} = format_array(Next(), [], Opts, Level + 1),
|
||||
{Continue, [?start_array, Array, ?end_array]};
|
||||
format_something({event, {Type, Value}, Next}, _Opts, _Level) ->
|
||||
{Next, [encode(Type, Value)]}.
|
||||
|
||||
prettify({event, start_array, Next}, Acc, Opts, Level, start) ->
|
||||
prettify(Next(), [Acc, ?start_array], Opts, Level + 1, new);
|
||||
prettify({event, start_array, Next}, Acc, Opts, Level, _) ->
|
||||
prettify(Next(),
|
||||
[Acc, ?comma, space(Opts), indent(Opts, Level), ?start_array],
|
||||
Opts,
|
||||
Level + 1,
|
||||
new);
|
||||
|
||||
prettify({event, end_object, Next}, Acc, Opts, Level, value) ->
|
||||
DeLevel = Level - 1,
|
||||
prettify(Next(), [Acc, indent(Opts, DeLevel), ?end_object], Opts, DeLevel, value);
|
||||
prettify({event, end_object, Next}, Acc, Opts, Level, new) ->
|
||||
prettify(Next(), [Acc, ?end_object], Opts, Level - 1, value);
|
||||
format_object({event, end_object, Next}, Acc, _Opts, _Level) ->
|
||||
{Next, Acc};
|
||||
format_object({event, {key, Key}, Next}, Acc, Opts, Level) ->
|
||||
{Continue, Value} = format_something(Next(), Opts, Level),
|
||||
case Continue() of
|
||||
{event, end_object, NextNext} ->
|
||||
{NextNext, [Acc, indent(Opts, Level), encode(string, Key), ?colon, space(Opts), Value]}
|
||||
; Else ->
|
||||
format_object(Else,
|
||||
[Acc, indent(Opts, Level), encode(string, Key), ?colon, space(Opts), Value, ?comma],
|
||||
Opts,
|
||||
Level
|
||||
)
|
||||
end.
|
||||
|
||||
prettify({event, end_array, Next}, Acc, Opts, Level, value) ->
|
||||
DeLevel = Level - 1,
|
||||
prettify(Next(), [Acc, indent(Opts, DeLevel), ?end_array], Opts, DeLevel, value);
|
||||
prettify({event, end_array, Next}, Acc, Opts, Level, new) ->
|
||||
prettify(Next(), [Acc, ?end_array], Opts, Level - 1, value);
|
||||
format_array({event, end_array, Next}, Acc, _Opts, _Level) ->
|
||||
{Next, Acc};
|
||||
format_array(Event, Acc, Opts, Level) ->
|
||||
{Continue, Value} = format_something(Event, Opts, Level),
|
||||
case Continue() of
|
||||
{event, end_array, NextNext} ->
|
||||
{NextNext, [Acc, indent(Opts, Level), Value]}
|
||||
; Else ->
|
||||
format_array(Else, [Acc, indent(Opts, Level), Value, ?comma], Opts, Level)
|
||||
end.
|
||||
|
||||
prettify({event, {key, Key}, Next}, Acc, Opts, Level, value) ->
|
||||
prettify(Next(),
|
||||
[Acc, ?comma, space(Opts), indent(Opts, Level), format(string, Key), ?colon, space(Opts)],
|
||||
Opts,
|
||||
Level,
|
||||
key);
|
||||
prettify({event, {key, Key}, Next}, Acc, Opts, Level, _) ->
|
||||
prettify(Next(),
|
||||
[Acc, indent(Opts, Level), format(string, Key), ?colon, space(Opts)],
|
||||
Opts,
|
||||
Level,
|
||||
key);
|
||||
|
||||
prettify({event, {Type, Value}, Next}, Acc, Opts, Level, value) ->
|
||||
prettify(Next(),
|
||||
[Acc, ?comma, space(Opts), indent(Opts, Level), format(Type, Value)],
|
||||
Opts,
|
||||
Level,
|
||||
value);
|
||||
prettify({event, {Type, Value}, Next}, Acc, Opts, Level, new) ->
|
||||
prettify(Next(), [Acc, indent(Opts, Level), format(Type, Value)], Opts, Level, value);
|
||||
prettify({event, {Type, Value}, Next}, Acc, Opts, Level, key) ->
|
||||
prettify(Next(), [Acc, format(Type, Value)], Opts, Level, value);
|
||||
prettify({event, {Type, Value}, Next}, _Acc, Opts, Level, start) ->
|
||||
case Opts#opts.strict of
|
||||
true -> erlang:throw(badarg)
|
||||
; false -> prettify(Next(), [format(Type, Value)], Opts, Level, error)
|
||||
end;
|
||||
-define(is_utf_encoding(X),
|
||||
X == utf8; X == utf16; X == utf32; X == {utf16, little}; X == {utf32, little}
|
||||
).
|
||||
|
||||
prettify({event, end_json, Next}, Acc, Opts, _, _) ->
|
||||
case Next() of
|
||||
{incomplete, More} -> case More(end_stream) of
|
||||
ok -> encode(Acc, Opts)
|
||||
; _ -> erlang:throw(badarg)
|
||||
end
|
||||
encode(Acc, Opts) when is_list(Acc) ->
|
||||
case Opts#opts.output_encoding of
|
||||
iolist -> Acc
|
||||
; UTF when ?is_utf_encoding(UTF) -> unicode:characters_to_binary(Acc, utf8, UTF)
|
||||
; _ -> erlang:throw(badarg)
|
||||
end;
|
||||
|
||||
prettify(_, _, _, _, error) -> erlang:throw(badarg).
|
||||
|
||||
format(string, String) ->
|
||||
encode(string, String) ->
|
||||
[?quote, String, ?quote];
|
||||
format(literal, Literal) ->
|
||||
encode(literal, Literal) ->
|
||||
erlang:atom_to_list(Literal);
|
||||
format(_, Number) ->
|
||||
encode(_, Number) ->
|
||||
Number.
|
||||
|
||||
|
||||
|
@ -176,15 +158,3 @@ space(Opts) ->
|
|||
0 -> []
|
||||
; X when X > 0 -> [ ?space || _ <- lists:seq(1, X) ]
|
||||
end.
|
||||
|
||||
|
||||
-define(is_utf_encoding(X),
|
||||
X == utf8; X == utf16; X == utf32; X == {utf16, little}; X == {utf32, little}
|
||||
).
|
||||
|
||||
encode(Acc, Opts) ->
|
||||
case Opts#opts.output_encoding of
|
||||
iolist -> Acc
|
||||
; UTF when ?is_utf_encoding(UTF) -> unicode:characters_to_binary(Acc, utf8, UTF)
|
||||
; _ -> erlang:throw(badarg)
|
||||
end.
|
|
@ -49,16 +49,9 @@ collect_strict({event, start_array, Next}, Keys) ->
|
|||
collect_strict(_, _) ->
|
||||
false.
|
||||
|
||||
%% make sure to ensure tail is clean
|
||||
collect({event, end_json, Next}, _Keys) ->
|
||||
case Next() of
|
||||
{incomplete, More} -> case More(end_stream) of
|
||||
ok -> true
|
||||
; _ -> false
|
||||
end
|
||||
; _ -> false
|
||||
end;
|
||||
|
||||
collect({event, end_json, _Next}, _Keys) ->
|
||||
true;
|
||||
|
||||
%% check to see if key has already been encountered, if not add it to the key accumulator
|
||||
%% and continue, else return false
|
||||
|
|
|
@ -26,6 +26,14 @@
|
|||
|
||||
-export([main/1]).
|
||||
|
||||
-define(to_json(X, Y, N),
|
||||
etap:is(jsx:term_to_json(X), Y, N)
|
||||
).
|
||||
|
||||
-define(to_erep(X, Y, N),
|
||||
etap:is(jsx:json_to_term(X), Y, N)
|
||||
).
|
||||
|
||||
main([]) ->
|
||||
test("./test/cases");
|
||||
|
||||
|
@ -38,9 +46,20 @@ test(Dir) ->
|
|||
|
||||
ValidJSONTests = load_tests(Dir),
|
||||
|
||||
etap:plan((length(ValidJSONTests) * 10) + 1),
|
||||
run_tests(ValidJSONTests),
|
||||
etap:plan((length(ValidJSONTests) * 10) + 9),
|
||||
run_jsx_tests(ValidJSONTests),
|
||||
|
||||
etap:is(multi_decode(multi_json_body(), []), multi_test_result(), "multi terms"),
|
||||
|
||||
?to_erep(<<"{}">>, [{}], "empty object to erep"),
|
||||
?to_json([{}], <<"{}">>, "empty object to json"),
|
||||
?to_erep(<<"[]">>, [], "empty array to erep"),
|
||||
?to_json([], <<"[]">>, "empty array to json"),
|
||||
?to_erep(<<"{ \"key\": \"value\", \"another key\": [] }">>, [{<<"key">>, <<"value">>}, {<<"another key">>, []}], "object to erep"),
|
||||
?to_json([{<<"key">>, <<"value">>}, {<<"another key">>, []}], <<"{\"key\":\"value\",\"another key\":[]}">>, "object to json"),
|
||||
?to_erep(<<"[true, 1, -0.5e7, \"hello world\"]">>, [true, 1, -0.5e7, <<"hello world">>], "array to erep"),
|
||||
?to_json([true, 1, -0.5e7, <<"hello world">>], <<"[true,1,-5000000.0,\"hello world\"]">>, "array to json"),
|
||||
|
||||
etap:end_tests().
|
||||
|
||||
|
||||
|
@ -62,9 +81,9 @@ load_tests([Test|Rest], Dir, Acc) ->
|
|||
end
|
||||
catch _:_ -> load_tests(Rest, Dir, Acc) end.
|
||||
|
||||
run_tests([]) ->
|
||||
run_jsx_tests([]) ->
|
||||
ok;
|
||||
run_tests([{TestName, JSON, Events, Flags}|Rest]) ->
|
||||
run_jsx_tests([{TestName, JSON, Events, Flags}|Rest]) ->
|
||||
etap:is(decode(JSON, Flags), Events, TestName ++ ": utf8"),
|
||||
etap:is(incremental_decode(JSON, Flags), Events, TestName ++ ": incremental utf8"),
|
||||
etap:is(decode(to_utf16(JSON), Flags), Events, TestName ++ ": utf16"),
|
||||
|
@ -75,14 +94,14 @@ run_tests([{TestName, JSON, Events, Flags}|Rest]) ->
|
|||
etap:is(incremental_decode(to_utf32(JSON), Flags), Events, TestName ++ ": incremental utf32"),
|
||||
etap:is(decode(to_utf32le(JSON), Flags), Events, TestName ++ ": utf32le"),
|
||||
etap:is(incremental_decode(to_utf32le(JSON), Flags), Events, TestName ++ ": incremental utf32le"),
|
||||
run_tests(Rest).
|
||||
run_jsx_tests(Rest).
|
||||
|
||||
|
||||
decode(JSON, Flags) ->
|
||||
P = jsx:parser(Flags),
|
||||
decode_loop(P(JSON), []).
|
||||
|
||||
decode_loop({event, end_json, Next}, Acc) ->
|
||||
decode_loop({event, end_json, _Next}, Acc) ->
|
||||
lists:reverse([end_json] ++ Acc);
|
||||
decode_loop({incomplete, More}, Acc) ->
|
||||
decode_loop(More(end_stream), Acc);
|
||||
|
@ -98,7 +117,7 @@ incremental_decode_loop({incomplete, Next}, <<>>, Acc) ->
|
|||
incremental_decode_loop(Next(end_stream), <<>>, Acc);
|
||||
incremental_decode_loop({incomplete, Next}, <<C:1/binary, Rest/binary>>, Acc) ->
|
||||
incremental_decode_loop(Next(C), Rest, Acc);
|
||||
incremental_decode_loop({event, end_json, Next}, _Rest, Acc) ->
|
||||
incremental_decode_loop({event, end_json, _Next}, _Rest, Acc) ->
|
||||
lists:reverse([end_json] ++ Acc);
|
||||
incremental_decode_loop({event, Event, Next}, Rest, Acc) ->
|
||||
incremental_decode_loop(Next(), Rest, [Event] ++ Acc).
|
||||
|
@ -140,5 +159,3 @@ multi_test_result() ->
|
|||
[start_array, {integer, "1"}, {integer, "2"}, {integer, "3"}, end_array],
|
||||
[{string, "hope this works"}]
|
||||
].
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue