diff --git a/src/jsx.erl b/src/jsx.erl index 5448921..0401ce5 100644 --- a/src/jsx.erl +++ b/src/jsx.erl @@ -26,6 +26,7 @@ -export([encode/1, encode/2, decode/1, decode/2]). -export([is_json/1, is_json/2, is_term/1, is_term/2]). -export([format/1, format/2, minify/1, prettify/1]). +-export([consult/1, consult/2]). -export([encoder/3, decoder/3, parser/3]). -export([resume/3]). -export([maps_support/0]). @@ -110,6 +111,13 @@ is_term(Source) -> is_term(Source, []). is_term(Source, Config) -> jsx_verify:is_term(Source, Config). +-spec consult(File::file:name_all()) -> list(json_term()). +-spec consult(File::file:name_all(), Config::jsx_to_term:config()) -> list(json_term()). + +consult(File) -> consult(File, []). +consult(File, Config) -> jsx_consult:consult(File, Config). + + -type decoder() :: fun((json_text() | end_stream | end_json) -> any()). -spec decoder(Handler::module(), State::any(), Config::list()) -> decoder(). diff --git a/src/jsx_config.erl b/src/jsx_config.erl index b720033..76e1139 100644 --- a/src/jsx_config.erl +++ b/src/jsx_config.erl @@ -63,6 +63,8 @@ parse_config([unescaped_jsonp|Rest], Config) -> parse_config(Rest, Config#config{unescaped_jsonp=true}); parse_config([dirty_strings|Rest], Config) -> parse_config(Rest, Config#config{dirty_strings=true}); +parse_config([multi_term|Rest], Config) -> + parse_config(Rest, Config#config{multi_term=true}); %% retained for backwards compat, now does nothing however parse_config([repeat_keys|Rest], Config) -> parse_config(Rest, Config); @@ -152,6 +154,7 @@ valid_flags() -> escaped_strings, unescaped_jsonp, dirty_strings, + multi_term, repeat_keys, strict, stream, @@ -192,6 +195,7 @@ config_test_() -> escaped_strings = true, unescaped_jsonp = true, dirty_strings = true, + multi_term = true, strict_comments = true, strict_commas = true, strict_utf8 = true, @@ -204,6 +208,7 @@ config_test_() -> escaped_strings, unescaped_jsonp, dirty_strings, + multi_term, repeat_keys, strict, stream, @@ -275,6 +280,7 @@ config_to_list_test_() -> escaped_strings, unescaped_jsonp, dirty_strings, + multi_term, stream, uescape, strict @@ -284,6 +290,7 @@ config_to_list_test_() -> escaped_strings = true, unescaped_jsonp = true, dirty_strings = true, + multi_term = true, strict_comments = true, strict_utf8 = true, strict_single_quotes = true, diff --git a/src/jsx_config.hrl b/src/jsx_config.hrl index 3b87da1..9b5e025 100644 --- a/src/jsx_config.hrl +++ b/src/jsx_config.hrl @@ -1,8 +1,8 @@ -record(config, { + dirty_strings = false :: boolean(), escaped_forward_slashes = false :: boolean(), escaped_strings = false :: boolean(), - unescaped_jsonp = false :: boolean(), - dirty_strings = false :: boolean(), + multi_term = false :: boolean(), strict_comments = false :: boolean(), strict_commas = false :: boolean(), strict_utf8 = false :: boolean(), @@ -10,6 +10,7 @@ strict_escapes = false :: boolean(), stream = false :: boolean(), uescape = false :: boolean(), + unescaped_jsonp = false :: boolean(), error_handler = false :: false | jsx_config:handler(), incomplete_handler = false :: false | jsx_config:handler() }). diff --git a/src/jsx_consult.erl b/src/jsx_consult.erl new file mode 100644 index 0000000..0c9284e --- /dev/null +++ b/src/jsx_consult.erl @@ -0,0 +1,99 @@ +%% The MIT License + +%% Copyright (c) 2010-2015 Alisdair Sullivan + +%% Permission is hereby granted, free of charge, to any person obtaining a copy +%% of this software and associated documentation files (the "Software"), to deal +%% in the Software without restriction, including without limitation the rights +%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +%% copies of the Software, and to permit persons to whom the Software is +%% furnished to do so, subject to the following conditions: + +%% The above copyright notice and this permission notice shall be included in +%% all copies or substantial portions of the Software. + +%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +%% THE SOFTWARE. + + +-module(jsx_consult). + +-export([consult/2]). +-export([init/1, reset/1, handle_event/2]). + + +-record(config, { + labels = binary, + return_maps = false +}). + +-type config() :: list(). +-export_type([config/0]). + +-ifndef(maps_support). +-type json_value() :: list(json_value()) + | list({binary() | atom(), json_value()}) + | true + | false + | null + | integer() + | float() + | binary(). +-endif. + +-ifdef(maps_support). +-type json_value() :: list(json_value()) + | map() + | true + | false + | null + | integer() + | float() + | binary(). +-endif. + + +-spec consult(File::file:name_all(), Config::config()) -> [json_value()]. + +-ifdef(maps_always). +opts(Opts) -> [return_maps, multi_term] ++ Opts. +-endif. +-ifndef(maps_always). +opts(Opts) -> [multi_term] ++ Opts. +-endif. + +consult(File, Config) when is_list(Config) -> + case file:read_file(File) of + {ok, Bin} -> + {Final, _, _} = (jsx:decoder( + ?MODULE, + opts(Config), + jsx_config:extract_config(opts(Config)) + ))(Bin), + lists:reverse(Final); + {error, _} -> erlang:error(badarg) + end. + + +-type state() :: {list(), #config{}}. +-spec init(Config::proplists:proplist()) -> state(). + +init(Config) -> {[], Config, jsx_to_term:start_term(Config)}. + + +-spec reset(State::state()) -> state(). + +reset({Acc, Config, _}) -> {Acc, Config, jsx_to_term:start_term(Config)}. + + +-spec handle_event(Event::any(), State::state()) -> state(). + +handle_event(end_json, {Acc, Config, State}) -> + {[jsx_to_term:get_value(State)] ++ Acc, Config, State}; +handle_event(Event, {Acc, Config, State}) -> + {Acc, Config, jsx_to_term:handle_event(Event, State)}. diff --git a/src/jsx_decoder.erl b/src/jsx_decoder.erl index da3bdd2..e6b4152 100644 --- a/src/jsx_decoder.erl +++ b/src/jsx_decoder.erl @@ -1130,6 +1130,8 @@ done(<>, Handler, Stack, Config) -> done(<<>>, {Handler, State}, [], Config=#config{stream=true}) -> incomplete(done, <<>>, {Handler, State}, [], Config); done(<<>>, {_Handler, State}, [], _Config) -> State; +done(Bin, {Handler, State}, _Stack, Config=#config{multi_term=true}) -> + value(Bin, {Handler, Handler:reset(State)}, [], Config); done(Bin, Handler, Stack, Config) -> ?error(done, Bin, Handler, Stack, Config).