diff --git a/README.md b/README.md index dbe1a16..35ce425 100644 --- a/README.md +++ b/README.md @@ -387,10 +387,6 @@ additional options beyond these. see control codes and problematic codepoints and replacing them with the appropriate escapes -- `repeat_keys` - - this flag circumvents checking for repeated keys in generated json - - `stream` see [incomplete input](#incomplete-input) diff --git a/src/jsx_config.erl b/src/jsx_config.erl index f7b8d7a..b720033 100644 --- a/src/jsx_config.erl +++ b/src/jsx_config.erl @@ -63,12 +63,14 @@ 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}); +%% retained for backwards compat, now does nothing however parse_config([repeat_keys|Rest], Config) -> - parse_config(Rest, Config#config{repeat_keys=true}); + parse_config(Rest, Config); parse_config([uescape|Rest], Config) -> parse_config(Rest, Config#config{uescape=true}); parse_config([strict|Rest], Config) -> - parse_config(Rest, Config#config{strict_comments=true, + parse_config(Rest, Config#config{ + strict_comments=true, strict_commas=true, strict_utf8=true, strict_single_quotes=true, @@ -190,7 +192,6 @@ config_test_() -> escaped_strings = true, unescaped_jsonp = true, dirty_strings = true, - repeat_keys = true, strict_comments = true, strict_commas = true, strict_utf8 = true, @@ -274,7 +275,6 @@ config_to_list_test_() -> escaped_strings, unescaped_jsonp, dirty_strings, - repeat_keys, stream, uescape, strict @@ -284,7 +284,6 @@ config_to_list_test_() -> escaped_strings = true, unescaped_jsonp = true, dirty_strings = true, - repeat_keys = true, strict_comments = true, strict_utf8 = true, strict_single_quotes = true, diff --git a/src/jsx_config.hrl b/src/jsx_config.hrl index e72247a..3b87da1 100644 --- a/src/jsx_config.hrl +++ b/src/jsx_config.hrl @@ -3,7 +3,6 @@ escaped_strings = false :: boolean(), unescaped_jsonp = false :: boolean(), dirty_strings = false :: boolean(), - repeat_keys = false :: boolean(), strict_comments = false :: boolean(), strict_commas = false :: boolean(), strict_utf8 = false :: boolean(), diff --git a/src/jsx_parser.erl b/src/jsx_parser.erl index da128ff..4fea49d 100644 --- a/src/jsx_parser.erl +++ b/src/jsx_parser.erl @@ -88,7 +88,7 @@ handle_event(Event, {Handler, State}, _Config) -> {Handler, Handler:handle_event value([start_object|Tokens], Handler, Stack, Config) -> - object(Tokens, handle_event(start_object, Handler, Config), [{object, sets:new()}|Stack], Config); + object(Tokens, handle_event(start_object, Handler, Config), [object|Stack], Config); value([start_array|Tokens], Handler, Stack, Config) -> array(Tokens, handle_event(start_array, Handler, Config), [array|Stack], Config); value([{literal, Literal}|Tokens], Handler, Stack, Config) when Literal == true; Literal == false; Literal == null -> @@ -136,35 +136,19 @@ value(BadTokens, Handler, Stack, Config) when is_list(BadTokens) -> value(Token, Handler, Stack, Config) -> value([Token], Handler, Stack, Config). -object([end_object|Tokens], Handler, [{object, _}|Stack], Config) -> +object([end_object|Tokens], Handler, [object|Stack], Config) -> maybe_done(Tokens, handle_event(end_object, Handler, Config), Stack, Config); object([{key, Key}|Tokens], Handler, Stack, Config) when is_atom(Key); is_binary(Key); is_integer(Key) -> object([Key|Tokens], Handler, Stack, Config); -object([Key|Tokens], Handler, [{object, _Keys}|Stack], Config=#config{repeat_keys=true}) +object([Key|Tokens], Handler, [object|Stack], Config) when is_atom(Key); is_binary(Key); is_integer(Key) -> try clean_string(fix_key(Key), Config) of K -> value( Tokens, handle_event({key, K}, Handler, Config), - [{object, []}|Stack], - Config - ) - catch error:badarg -> - ?error(object, [{string, Key}|Tokens], Handler, Stack, Config) - end; -object([Key|Tokens], Handler, [{object, Keys}|Stack], Config) -when is_atom(Key); is_binary(Key); is_integer(Key) -> - try - CleanKey = clean_string(fix_key(Key), Config), - case sets:is_element(CleanKey, Keys) of true -> erlang:error(badarg); _ -> ok end, - CleanKey - of K -> - value( - Tokens, - handle_event({key, K}, Handler, Config), - [{object, sets:add_element(K, Keys)}|Stack], + [object|Stack], Config ) catch error:badarg -> @@ -186,7 +170,7 @@ array(Token, Handler, Stack, Config) -> maybe_done([end_json], Handler, [], Config) -> done([end_json], Handler, [], Config); -maybe_done(Tokens, Handler, [{object, _}|_] = Stack, Config) when is_list(Tokens) -> +maybe_done(Tokens, Handler, [object|_] = Stack, Config) when is_list(Tokens) -> object(Tokens, Handler, Stack, Config); maybe_done(Tokens, Handler, [array|_] = Stack, Config) when is_list(Tokens) -> array(Tokens, Handler, Stack, Config); @@ -1057,6 +1041,7 @@ json_escape_sequence_test_() -> {"json escape sequence test - 16#def", ?_assertEqual(json_escape_sequence(16#def), "\\u0def")} ]. + uescape_test_() -> [ {"\"\\u0080\"", ?_assertEqual( @@ -1080,6 +1065,7 @@ uescape_test_() -> )} ]. + fix_key_test_() -> [ {"binary key", ?_assertEqual(fix_key(<<"foo">>), <<"foo">>)}, @@ -1088,16 +1074,6 @@ fix_key_test_() -> ]. -repeated_key_test_() -> - Parse = fun(Events, Config) -> (parser(?MODULE, [], Config))(Events ++ [end_json]) end, - [ - {"repeated key", ?_assertError( - badarg, - Parse([start_object, <<"key">>, true, <<"key">>, true, end_object], []) - )} - ]. - - datetime_test_() -> [ {"datetime", ?_assertEqual( diff --git a/src/jsx_verify.erl b/src/jsx_verify.erl index 09a668d..2073b99 100644 --- a/src/jsx_verify.erl +++ b/src/jsx_verify.erl @@ -27,15 +27,7 @@ -export([init/1, handle_event/2]). --record(config, { - repeated_keys = true -}). - --type config() :: []. --export_type([config/0]). - - --spec is_json(Source::binary(), Config::config()) -> true | false | {incomplete, jsx:decoder()}. +-spec is_json(Source::binary(), Config::jsx_config:config()) -> true | false | {incomplete, jsx:decoder()}. is_json(Source, Config) when is_list(Config) -> try (jsx:decoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source) @@ -43,7 +35,7 @@ is_json(Source, Config) when is_list(Config) -> end. --spec is_term(Source::any(), Config::config()) -> true | false | {incomplete, jsx:encoder()}. +-spec is_term(Source::any(), Config::jsx_config:config()) -> true | false | {incomplete, jsx:encoder()}. is_term(Source, Config) when is_list(Config) -> try (jsx:encoder(?MODULE, Config, jsx_config:extract_config(Config)))(Source) @@ -51,15 +43,15 @@ is_term(Source, Config) when is_list(Config) -> end. -parse_config(Config) -> parse_config(Config, #config{}). +parse_config(Config) -> parse_config(Config, []). +%% ignore deprecated flags parse_config([no_repeated_keys|Rest], Config) -> - parse_config(Rest, Config#config{repeated_keys=false}); -%% deprecated, use `no_repeated_keys` + parse_config(Rest, Config); parse_config([{repeated_keys, Val}|Rest], Config) when Val == true; Val == false -> - parse_config(Rest, Config#config{repeated_keys=Val}); + parse_config(Rest, Config); parse_config([repeated_keys|Rest], Config) -> - parse_config(Rest, Config#config{repeated_keys=true}); + parse_config(Rest, Config); parse_config([{K, _}|Rest] = Options, Config) -> case lists:member(K, jsx_config:valid_flags()) of true -> parse_config(Rest, Config); @@ -73,27 +65,18 @@ parse_config([K|Rest] = Options, Config) -> parse_config([], Config) -> Config. --type state() :: {#config{}, any()}. + +%% we don't actually need any state for this +-type state() :: []. -spec init(Config::proplists:proplist()) -> state(). -init(Config) -> {parse_config(Config), []}. +init(Config) -> parse_config(Config). -spec handle_event(Event::any(), State::state()) -> state(). handle_event(end_json, _) -> true; -handle_event(_, {Config, _} = State) when Config#config.repeated_keys == true -> State; - -handle_event(start_object, {Config, Keys}) -> {Config, [dict:new()] ++ Keys}; -handle_event(end_object, {Config, [_|Keys]}) -> {Config, Keys}; - -handle_event({key, Key}, {Config, [CurrentKeys|Keys]}) -> - case dict:is_key(Key, CurrentKeys) of - true -> erlang:error(badarg); - false -> {Config, [dict:store(Key, blah, CurrentKeys)|Keys]} - end; - handle_event(_, State) -> State. @@ -105,15 +88,15 @@ handle_event(_, State) -> State. config_test_() -> [ - {"empty config", ?_assertEqual(#config{}, parse_config([]))}, - {"no repeat keys", ?_assertEqual(#config{repeated_keys=false}, parse_config([no_repeated_keys]))}, - {"bare repeated keys", ?_assertEqual(#config{}, parse_config([repeated_keys]))}, + {"empty config", ?_assertEqual([], parse_config([]))}, + {"no repeat keys", ?_assertEqual([], parse_config([no_repeated_keys]))}, + {"bare repeated keys", ?_assertEqual([], parse_config([repeated_keys]))}, {"repeated keys true", ?_assertEqual( - #config{}, + [], parse_config([{repeated_keys, true}]) )}, {"repeated keys false", ?_assertEqual( - #config{repeated_keys=false}, + [], parse_config([{repeated_keys, false}]) )}, {"invalid opt flag", ?_assertError(badarg, parse_config([error]))}, @@ -121,50 +104,13 @@ config_test_() -> ]. -repeated_keys_test_() -> - RepeatedKey = [ - start_object, - {key, <<"alpha">>}, - {literal, true}, - {key, <<"alpha">>}, - {literal, false}, - end_object, - end_json - ], - NestedKey = [ - start_object, - {key, <<"alpha">>}, - start_object, - {key, <<"alpha">>}, - start_object, - {key, <<"alpha">>}, - {literal, true}, - end_object, - end_object, - end_object, - end_json - ], - [ - {"repeated key", ?_assert( - lists:foldl(fun handle_event/2, {#config{}, []}, RepeatedKey) - )}, - {"no repeated key", ?_assertError( - badarg, - lists:foldl(fun handle_event/2, {#config{repeated_keys=false}, []}, RepeatedKey) - )}, - {"nested key", ?_assert( - lists:foldl(fun handle_event/2, {#config{repeated_keys=false}, []}, NestedKey) - )} - ]. - - handle_event_test_() -> Data = jsx:test_cases() ++ jsx:special_test_cases(), [ { Title, ?_assertEqual( true, - lists:foldl(fun handle_event/2, {#config{}, []}, Events ++ [end_json]) + lists:foldl(fun handle_event/2, [], Events ++ [end_json]) ) } || {Title, _, _, Events} <- Data ].