first attempt at integrating json lib with jsx lib

This commit is contained in:
alisdair sullivan 2010-08-03 20:29:49 -07:00
parent 4c378791f9
commit 3ea3eba7b3
7 changed files with 96 additions and 124 deletions

View file

@ -16,4 +16,4 @@ clean:
./priv/backends.escript clean
install: compile
./rebar install
./rebar -f install

View file

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

View file

@ -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

View file

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

View file

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

View file

@ -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

View file

@ -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"}]
].