new encoder
This commit is contained in:
parent
6dacf64620
commit
9a968548c8
1 changed files with 99 additions and 151 deletions
|
@ -23,13 +23,14 @@
|
||||||
|
|
||||||
-module(jsx_encoder).
|
-module(jsx_encoder).
|
||||||
|
|
||||||
-export([encoder/1]).
|
-export([encoder/3]).
|
||||||
|
|
||||||
|
|
||||||
-spec encoder(OptsList::jsx:opts()) -> jsx:encoder().
|
-spec encoder(Mod::module(), Args::any(), Opts::jsx:opts()) -> jsx:encoder().
|
||||||
|
|
||||||
|
encoder(Mod, Args, Opts) ->
|
||||||
|
fun(JSON) -> start(JSON, {Mod, Mod:init(Args)}, jsx_utils:parse_opts(Opts)) end.
|
||||||
|
|
||||||
encoder(OptsList) ->
|
|
||||||
fun(Forms) -> start(Forms, [], [], jsx_utils:parse_opts(OptsList)) end.
|
|
||||||
|
|
||||||
|
|
||||||
-include("../include/jsx_opts.hrl").
|
-include("../include/jsx_opts.hrl").
|
||||||
|
@ -42,171 +43,118 @@ encoder(OptsList) ->
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
|
|
||||||
-ifndef(incomplete).
|
start(Term, {Handler, State}, Opts) ->
|
||||||
-define(incomplete(State, T, Stack, Opts),
|
Handler:handle_event(end_json, value(Term, {Handler, State}, Opts)).
|
||||||
{incomplete, fun(Stream) when is_list(Stream) ->
|
|
||||||
State(Stream, T, Stack, Opts)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
).
|
|
||||||
-endif.
|
|
||||||
|
|
||||||
|
|
||||||
-ifndef(event).
|
value(String, {Handler, State}, Opts) when is_binary(String) ->
|
||||||
-define(event(Event, State, Rest, T, Stack, Opts),
|
Handler:handle_event({string, jsx_utils:json_escape(String, Opts)}, State);
|
||||||
State(Rest, Event ++ T, Stack, Opts)
|
value(Float, {Handler, State}, _Opts) when is_float(Float) ->
|
||||||
).
|
Handler:handle_event({float, Float}, State);
|
||||||
-endif.
|
value(Int, {Handler, State}, _Opts) when is_integer(Int) ->
|
||||||
|
Handler:handle_event({integer, Int}, State);
|
||||||
|
value(Literal, {Handler, State}, _Opts)
|
||||||
|
when Literal == true; Literal == false; Literal == null ->
|
||||||
|
Handler:handle_event({literal, Literal}, State);
|
||||||
|
value([{}], {Handler, State}, _Opts) ->
|
||||||
|
Handler:handle_event(end_object, Handler:handle_event(start_object, State));
|
||||||
|
value([], {Handler, State}, _Opts) ->
|
||||||
|
Handler:handle_event(end_array, Handler:handle_event(start_array, State));
|
||||||
|
value(List, {Handler, State}, Opts) when is_list(List) ->
|
||||||
|
list_or_object(List, {Handler, State}, Opts);
|
||||||
|
value(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
||||||
|
|
||||||
|
|
||||||
|
list_or_object([Tuple|_] = List, {Handler, State}, Opts) when is_tuple(Tuple) ->
|
||||||
|
object(List, {Handler, Handler:handle_event(start_object, State)}, Opts);
|
||||||
|
list_or_object(List, {Handler, State}, Opts) ->
|
||||||
|
list(List, {Handler, Handler:handle_event(start_array, State)}, Opts).
|
||||||
|
|
||||||
|
|
||||||
start([{string, String}], [], [], Opts) when is_binary(String) ->
|
object([{Key, Value}|Rest], {Handler, State}, Opts) ->
|
||||||
{ok, [{string, jsx_utils:json_escape(String, Opts)}, end_json]};
|
object(Rest, {Handler,
|
||||||
start([{float, Float}], [], [], _Opts) when is_float(Float) ->
|
value(Value, {Handler, Handler:handle_event({key, Key}, State)}, Opts)
|
||||||
{ok, [{float, Float}, end_json]};
|
}, Opts);
|
||||||
start([{integer, Int}], [], [], _Opts) when is_integer(Int) ->
|
object([], {Handler, State}, _Opts) -> Handler:handle_event(end_object, State);
|
||||||
{ok, [{integer, Int}, end_json]};
|
object(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
||||||
start([{literal, Atom}], [], [], _Opts)
|
|
||||||
when Atom == true; Atom == false; Atom == null ->
|
|
||||||
{ok, [{literal, Atom}, end_json]};
|
|
||||||
%% third parameter is a stack to match end_foos to start_foos
|
|
||||||
start(Forms, [], [], Opts) when is_list(Forms) ->
|
|
||||||
list_or_object(Forms, [], [], Opts);
|
|
||||||
start(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
list_or_object([start_object|Forms], T, Stack, Opts) ->
|
list([Value|Rest], {Handler, State}, Opts) ->
|
||||||
?event([start_object], key, Forms, T, [object] ++ Stack, Opts);
|
list(Rest, {Handler, value(Value, {Handler, State}, Opts)}, Opts);
|
||||||
list_or_object([start_array|Forms], T, Stack, Opts) ->
|
list([], {Handler, State}, _Opts) -> Handler:handle_event(end_array, State);
|
||||||
?event([start_array], value, Forms, T, [array] ++ Stack, Opts);
|
list(Term, Handler, Opts) -> ?error([Term, Handler, Opts]).
|
||||||
list_or_object([], T, Stack, Opts) ->
|
|
||||||
?incomplete(list_or_object, T, Stack, Opts);
|
|
||||||
list_or_object(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
key([{key, Key}|Forms], T, Stack, Opts) when is_binary(Key) ->
|
|
||||||
?event([{key,
|
|
||||||
unicode:characters_to_binary(jsx_utils:json_escape(Key, Opts))
|
|
||||||
}],
|
|
||||||
value, Forms, T, Stack, Opts
|
|
||||||
);
|
|
||||||
key([end_object|Forms], T, [object|Stack], Opts) ->
|
|
||||||
?event([end_object], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
key([], T, Stack, Opts) -> ?incomplete(key, T, Stack, Opts);
|
|
||||||
key(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
value([{string, S}|Forms], T, Stack, Opts) when is_binary(S) ->
|
|
||||||
?event([{string, jsx_utils:json_escape(S, Opts)}],
|
|
||||||
maybe_done, Forms, T, Stack, Opts
|
|
||||||
);
|
|
||||||
value([{float, F}|Forms], T, Stack, Opts) when is_float(F) ->
|
|
||||||
?event([{float, F}], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
value([{integer, I}|Forms], T, Stack, Opts) when is_integer(I) ->
|
|
||||||
?event([{integer, I}], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
value([{literal, L}|Forms], T, Stack, Opts)
|
|
||||||
when L == true; L == false; L == null ->
|
|
||||||
?event([{literal, L}], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
value([start_object|Forms], T, Stack, Opts) ->
|
|
||||||
?event([start_object], key, Forms, T, [object] ++ Stack, Opts);
|
|
||||||
value([start_array|Forms], T, Stack, Opts) ->
|
|
||||||
?event([start_array], maybe_done, Forms, T, [array] ++ Stack, Opts);
|
|
||||||
value([end_array|Forms], T, [array|Stack], Opts) ->
|
|
||||||
?event([end_array], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
value([], T, Stack, Opts) -> ?incomplete(value, T, Stack, Opts);
|
|
||||||
value(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
maybe_done([end_json], T, [], Opts) ->
|
|
||||||
?event([end_json], done, [], T, [], Opts);
|
|
||||||
maybe_done([end_object|Forms], T, [object|Stack], Opts) ->
|
|
||||||
?event([end_object], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
maybe_done([end_array|Forms], T, [array|Stack], Opts) ->
|
|
||||||
?event([end_array], maybe_done, Forms, T, Stack, Opts);
|
|
||||||
maybe_done(Forms, T, [object|_] = Stack, Opts) -> key(Forms, T, Stack, Opts);
|
|
||||||
maybe_done(Forms, T, [array|_] = Stack, Opts) -> value(Forms, T, Stack, Opts);
|
|
||||||
maybe_done([], T, Stack, Opts) -> ?incomplete(maybe_done, T, Stack, Opts);
|
|
||||||
maybe_done(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
done([], T, [], _Opts) -> lists:reverse(T);
|
|
||||||
done(Forms, T, Stack, Opts) -> ?error([Forms, T, Stack, Opts]).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
encode(Terms) ->
|
encode(Term) -> (encoder(jsx, [], []))(Term).
|
||||||
try case (jsx:encoder([]))(Terms) of
|
|
||||||
Terms -> true
|
|
||||||
end
|
|
||||||
catch
|
|
||||||
error:badarg -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
encode_test_() ->
|
encode_test_() ->
|
||||||
[
|
[
|
||||||
{"empty object", ?_assert(encode([start_object, end_object, end_json]))},
|
{"naked string", ?_assert(encode(<<"a string">>)
|
||||||
{"empty array", ?_assert(encode([start_array, end_array, end_json]))},
|
=:= [{string, <<"a string">>}, end_json])
|
||||||
{"nested empty objects", ?_assert(encode([start_object,
|
},
|
||||||
{key, <<"empty object">>},
|
{"naked integer", ?_assert(encode(123)
|
||||||
start_object,
|
=:= [{integer, 123}, end_json])
|
||||||
{key, <<"empty object">>},
|
},
|
||||||
start_object,
|
{"naked float", ?_assert(encode(1.23)
|
||||||
end_object,
|
=:= [{float, 1.23}, end_json])
|
||||||
end_object,
|
},
|
||||||
end_object,
|
{"naked literal", ?_assert(encode(null)
|
||||||
end_json
|
=:= [{literal, null}, end_json])
|
||||||
]))},
|
},
|
||||||
{"nested empty arrays", ?_assert(encode([start_array,
|
{"empty object", ?_assert(encode([{}])
|
||||||
start_array,
|
=:= [start_object, end_object, end_json])
|
||||||
start_array,
|
},
|
||||||
|
{"empty list", ?_assert(encode([])
|
||||||
|
=:= [start_array, end_array, end_json])
|
||||||
|
},
|
||||||
|
{"simple list", ?_assert(encode([1,2,3,true,false])
|
||||||
|
=:= [start_array,
|
||||||
|
{integer, 1},
|
||||||
|
{integer, 2},
|
||||||
|
{integer, 3},
|
||||||
|
{literal, true},
|
||||||
|
{literal, false},
|
||||||
end_array,
|
end_array,
|
||||||
end_array,
|
end_json])
|
||||||
end_array,
|
},
|
||||||
end_json
|
{"simple object", ?_assert(encode([{<<"a">>, true}, {<<"b">>, false}])
|
||||||
]))},
|
=:= [start_object,
|
||||||
{"simple object", ?_assert(encode([start_object,
|
|
||||||
{key, <<"a">>},
|
{key, <<"a">>},
|
||||||
{string, <<"hello">>},
|
{literal, true},
|
||||||
{key, <<"b">>},
|
{key, <<"b">>},
|
||||||
{integer, 1},
|
{literal, false},
|
||||||
{key, <<"c">>},
|
|
||||||
{float, 1.0},
|
|
||||||
{key, <<"d">>},
|
|
||||||
{literal, true},
|
|
||||||
end_object,
|
end_object,
|
||||||
end_json
|
end_json])
|
||||||
]))},
|
},
|
||||||
{"simple array", ?_assert(encode([start_array,
|
{"complex term", ?_assert(encode([
|
||||||
{string, <<"hello">>},
|
{<<"a">>, true},
|
||||||
{integer, 1},
|
{<<"b">>, false},
|
||||||
{float, 1.0},
|
{<<"c">>, [1,2,3]},
|
||||||
|
{<<"d">>, [{<<"key">>, <<"value">>}]}
|
||||||
|
]) =:= [start_object,
|
||||||
|
{key, <<"a">>},
|
||||||
{literal, true},
|
{literal, true},
|
||||||
|
{key, <<"b">>},
|
||||||
|
{literal, false},
|
||||||
|
{key, <<"c">>},
|
||||||
|
start_array,
|
||||||
|
{integer, 1},
|
||||||
|
{integer, 2},
|
||||||
|
{integer, 3},
|
||||||
end_array,
|
end_array,
|
||||||
end_json
|
{key, <<"d">>},
|
||||||
]))},
|
start_object,
|
||||||
{"unbalanced array", ?_assertNot(encode([start_array,
|
{key, <<"key">>},
|
||||||
end_array,
|
{string, <<"value">>},
|
||||||
blerg,
|
end_object,
|
||||||
end_array,
|
end_object,
|
||||||
end_json
|
end_json])
|
||||||
]))},
|
}
|
||||||
{"naked string", ?_assert((jsx:encoder())([{string, <<"hello">>}])
|
|
||||||
=:= {ok, [{string, <<"hello">>}, end_json]}
|
|
||||||
)},
|
|
||||||
{"naked literal", ?_assert((jsx:encoder())([{literal, true}])
|
|
||||||
=:= {ok, [{literal, true}, end_json]}
|
|
||||||
)},
|
|
||||||
{"naked integer", ?_assert((jsx:encoder())([{integer, 1}])
|
|
||||||
=:= {ok, [{integer, 1}, end_json]}
|
|
||||||
)},
|
|
||||||
{"naked string", ?_assert((jsx:encoder())([{float, 1.0}])
|
|
||||||
=:= {ok, [{float, 1.0}, end_json]}
|
|
||||||
)}
|
|
||||||
].
|
].
|
||||||
|
|
||||||
-endif.
|
-endif.
|
Loading…
Add table
Add a link
Reference in a new issue