remove stream mode from jsx_terms and streamline options handling in jsx_terms to simplify migration to new api
This commit is contained in:
parent
3186223fd1
commit
01a8154ac4
2 changed files with 69 additions and 49 deletions
|
@ -125,7 +125,6 @@
|
||||||
|
|
||||||
-type decoder_opts() :: [decoder_opt()].
|
-type decoder_opts() :: [decoder_opt()].
|
||||||
-type decoder_opt() :: {strict, true | false}
|
-type decoder_opt() :: {strict, true | false}
|
||||||
| {stream, true | false}
|
|
||||||
| {encoding, supported_utf()}.
|
| {encoding, supported_utf()}.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,19 @@
|
||||||
-include("jsx_common.hrl").
|
-include("jsx_common.hrl").
|
||||||
|
|
||||||
|
|
||||||
-spec json_to_term(JSON::binary(), Opts::decoder_opts()) ->
|
-record(decoder_opts, {
|
||||||
|
strict = false,
|
||||||
|
encoding = auto
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
-spec json_to_term(JSON::binary(), OptsList::decoder_opts()) ->
|
||||||
jsx_term() | {jsx, incomplete, fun()}.
|
jsx_term() | {jsx, incomplete, fun()}.
|
||||||
|
|
||||||
json_to_term(JSON, Opts) ->
|
json_to_term(JSON, OptsList) ->
|
||||||
P = jsx:decoder([iterate] ++ extract_parser_opts(Opts)),
|
Opts = parse_opts(OptsList, #decoder_opts{}),
|
||||||
case proplists:get_value(strict, Opts, false) of
|
P = jsx:decoder([iterate, {encoding, Opts#decoder_opts.encoding}]),
|
||||||
|
case Opts#decoder_opts.strict of
|
||||||
true -> collect_strict(P(JSON), [[]], Opts)
|
true -> collect_strict(P(JSON), [[]], Opts)
|
||||||
; false -> collect(P(JSON), [[]], Opts)
|
; false -> collect(P(JSON), [[]], Opts)
|
||||||
end.
|
end.
|
||||||
|
@ -42,32 +49,72 @@ json_to_term(JSON, Opts) ->
|
||||||
%% the jsx formatter (pretty printer) can do most of the heavy lifting in
|
%% the jsx formatter (pretty printer) can do most of the heavy lifting in
|
||||||
%% converting erlang terms to json strings
|
%% converting erlang terms to json strings
|
||||||
|
|
||||||
-spec term_to_json(JSON::jsx_term(), Opts::encoder_opts()) ->
|
-record(encoder_opts, {
|
||||||
|
strict = false,
|
||||||
|
encoding = auto,
|
||||||
|
formatter_opts = []
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
-spec term_to_json(JSON::jsx_term(), OptsList::encoder_opts()) ->
|
||||||
binary() | {jsx, incomplete, fun()}.
|
binary() | {jsx, incomplete, fun()}.
|
||||||
|
|
||||||
term_to_json(List, Opts) ->
|
term_to_json(List, OptsList) ->
|
||||||
case proplists:get_value(strict, Opts, false) of
|
Opts = parse_opts(OptsList, #encoder_opts{}),
|
||||||
|
case Opts#encoder_opts.strict of
|
||||||
true when is_list(List) -> continue
|
true when is_list(List) -> continue
|
||||||
; true -> erlang:error(badarg)
|
; true -> erlang:error(badarg)
|
||||||
; false -> continue
|
; false -> continue
|
||||||
end,
|
end,
|
||||||
Encoding = proplists:get_value(encoding, Opts, utf8),
|
|
||||||
FOpts = [{output_encoding, Encoding}] ++ Opts,
|
|
||||||
case term_to_events(List) of
|
case term_to_events(List) of
|
||||||
L when is_tuple(L) -> jsx:format(L, FOpts)
|
L when is_tuple(L) ->
|
||||||
; L when is_list(L) -> jsx:format(lists:reverse(L), FOpts)
|
jsx:format(L, Opts#encoder_opts.formatter_opts)
|
||||||
|
; L when is_list(L) ->
|
||||||
|
jsx:format(lists:reverse(L), Opts#encoder_opts.formatter_opts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
extract_parser_opts(Opts) ->
|
parse_opts([{strict, Val}|Rest], Opts = #decoder_opts{})
|
||||||
extract_parser_opts(Opts, []).
|
when Val =:= true; Val =:= false ->
|
||||||
|
parse_opts(Rest, Opts#decoder_opts{strict = Val});
|
||||||
extract_parser_opts([], Acc) -> Acc;
|
parse_opts([strict|Rest], Opts = #decoder_opts{}) ->
|
||||||
extract_parser_opts([{K,V}|Rest], Acc) ->
|
parse_opts(Rest, Opts#decoder_opts{strict = true});
|
||||||
case lists:member(K, [encoding]) of
|
parse_opts([{strict, Val}|Rest], Opts = #encoder_opts{})
|
||||||
true -> [{K,V}] ++ Acc
|
when Val =:= true; Val =:= false ->
|
||||||
; false -> extract_parser_opts(Rest, Acc)
|
parse_opts(Rest, Opts#encoder_opts{strict = Val});
|
||||||
end.
|
parse_opts([strict|Rest], Opts = #encoder_opts{}) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{strict = true});
|
||||||
|
parse_opts([{encoding, Val}|Rest], Opts = #decoder_opts{})
|
||||||
|
when Val =:= auto; Val =:= utf8;
|
||||||
|
Val =:= utf16; Val =:= {utf16,little};
|
||||||
|
Val =:= utf32; Val =:= {utf32,little} ->
|
||||||
|
parse_opts(Rest, Opts#decoder_opts{encoding = Val});
|
||||||
|
parse_opts([encoding|Rest], Opts = #decoder_opts{}) ->
|
||||||
|
parse_opts(Rest, Opts#decoder_opts{encoding = auto});
|
||||||
|
parse_opts([{encoding, Val}|Rest], Opts = #encoder_opts{})
|
||||||
|
when Val =:= auto; Val =:= utf8;
|
||||||
|
Val =:= utf16; Val =:= {utf16,little};
|
||||||
|
Val =:= utf32; Val =:= {utf32,little} ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{encoding = Val});
|
||||||
|
parse_opts([encoding|Rest], Opts = #encoder_opts{}) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{encoding = auto});
|
||||||
|
parse_opts([{indent, Val}|Rest], Opts = #encoder_opts{formatter_opts = F})
|
||||||
|
when is_integer(Val) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{formatter_opts = [{indent, Val}] ++ F});
|
||||||
|
parse_opts([indent|Rest], Opts = #encoder_opts{formatter_opts = F}) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{formatter_opts = [{indent, 1}] ++ F});
|
||||||
|
parse_opts([{space, Val}|Rest], Opts = #encoder_opts{formatter_opts = F})
|
||||||
|
when is_integer(Val) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{formatter_opts = [{space, Val}] ++ F});
|
||||||
|
parse_opts([space|Rest], Opts = #encoder_opts{formatter_opts = F}) ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{formatter_opts = [{space, 1}] ++ F});
|
||||||
|
parse_opts([{output_encoding, Val}|Rest], Opts = #encoder_opts{formatter_opts = F})
|
||||||
|
when Val =:= utf8;
|
||||||
|
Val =:= utf16; Val =:= {utf16,little};
|
||||||
|
Val =:= utf32; Val =:= {utf32,little} ->
|
||||||
|
parse_opts(Rest, Opts#encoder_opts{formatter_opts = [{output_encoding, Val}] ++ F});
|
||||||
|
parse_opts([], Opts) ->
|
||||||
|
Opts.
|
||||||
|
|
||||||
|
|
||||||
%% ensure the first jsx event we get is start_object or start_array when running
|
%% ensure the first jsx event we get is start_object or start_array when running
|
||||||
|
@ -75,13 +122,6 @@ extract_parser_opts([{K,V}|Rest], Acc) ->
|
||||||
collect_strict({jsx, Start, Next}, Acc, Opts)
|
collect_strict({jsx, Start, Next}, Acc, Opts)
|
||||||
when Start =:= start_object; Start =:= start_array ->
|
when Start =:= start_object; Start =:= start_array ->
|
||||||
collect(Next(), [[]|Acc], Opts);
|
collect(Next(), [[]|Acc], Opts);
|
||||||
collect_strict({jsx, incomplete, More}, Acc, Opts) ->
|
|
||||||
case proplists:get_value(stream, Opts, false) of
|
|
||||||
true -> {jsx, incomplete, fun(JSON) ->
|
|
||||||
collect_strict(More(JSON), Acc, Opts)
|
|
||||||
end}
|
|
||||||
; false -> erlang:error(badarg)
|
|
||||||
end;
|
|
||||||
collect_strict(_, _, _) -> erlang:error(badarg).
|
collect_strict(_, _, _) -> erlang:error(badarg).
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,17 +156,10 @@ collect({jsx, {key, _} = PreKey, Next}, Acc, Opts) ->
|
||||||
collect(Next(), [Key] ++ Acc, Opts);
|
collect(Next(), [Key] ++ Acc, Opts);
|
||||||
%% if our returned event is {jsx, incomplete, ...} try to force end and return
|
%% if our returned event is {jsx, incomplete, ...} try to force end and return
|
||||||
%% the Event if one is returned
|
%% the Event if one is returned
|
||||||
collect({jsx, incomplete, More}, Acc, Opts) ->
|
collect({jsx, incomplete, More}, _Acc, Opts) ->
|
||||||
case More(end_stream) of
|
case More(end_stream) of
|
||||||
{jsx, Event, _Next} -> event(Event, Opts)
|
{jsx, Event, _Next} -> event(Event, Opts)
|
||||||
; _ ->
|
; _ -> erlang:error(badarg)
|
||||||
case proplists:get_value(stream, Opts, false) of
|
|
||||||
true ->
|
|
||||||
{jsx, incomplete,
|
|
||||||
fun(JSON) -> collect(More(JSON), Acc, Opts) end
|
|
||||||
}
|
|
||||||
; false -> erlang:error(badarg)
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
%% check acc to see if we're inside an object or an array. because inside an
|
%% check acc to see if we're inside an object or an array. because inside an
|
||||||
%% object context the events that fall this far are always preceded by a key
|
%% object context the events that fall this far are always preceded by a key
|
||||||
|
@ -437,16 +470,4 @@ escape_test_() ->
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
stream_test_() ->
|
|
||||||
[
|
|
||||||
{"streaming mode",
|
|
||||||
?_assert(begin
|
|
||||||
{jsx, incomplete, F} = json_to_term(<<"{">>,
|
|
||||||
[{stream, true}]
|
|
||||||
),
|
|
||||||
F(<<"}">>)
|
|
||||||
end =:= [{}])
|
|
||||||
}
|
|
||||||
].
|
|
||||||
|
|
||||||
-endif.
|
-endif.
|
Loading…
Add table
Add a link
Reference in a new issue