reworks is_json to be more lenient, optionally more strict
This commit is contained in:
parent
51076f2054
commit
f3aa254664
2 changed files with 82 additions and 27 deletions
|
@ -24,10 +24,10 @@
|
||||||
|
|
||||||
-define(is_utf_encoding(X),
|
-define(is_utf_encoding(X),
|
||||||
X == utf8
|
X == utf8
|
||||||
; X == utf16
|
; X == utf16
|
||||||
; X == utf32
|
; X == utf32
|
||||||
; X == {utf16, little}
|
; X == {utf16, little}
|
||||||
; X == {utf32, little}
|
; X == {utf32, little}
|
||||||
).
|
).
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,11 +35,11 @@
|
||||||
-type jsx_opt() :: {escaped_unicode, ascii | codepoint | none}
|
-type jsx_opt() :: {escaped_unicode, ascii | codepoint | none}
|
||||||
| {multi_term, true | false}
|
| {multi_term, true | false}
|
||||||
| {encoding, auto
|
| {encoding, auto
|
||||||
| utf8
|
| utf8
|
||||||
| utf16
|
| utf16
|
||||||
| {utf16, little}
|
| {utf16, little}
|
||||||
| utf32
|
| utf32
|
||||||
| {utf32, little}
|
| {utf32, little}
|
||||||
}.
|
}.
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,7 +123,9 @@
|
||||||
|
|
||||||
|
|
||||||
-type verify_opts() :: [verify_opt()].
|
-type verify_opts() :: [verify_opt()].
|
||||||
-type verify_opt() :: {encoding, auto | supported_utf()}.
|
-type verify_opt() :: {encoding, auto | supported_utf()}
|
||||||
|
| {repeated_keys, true | false}
|
||||||
|
| {naked_values, true | false}.
|
||||||
|
|
||||||
|
|
||||||
-type format_opts() :: [format_opt()].
|
-type format_opts() :: [format_opt()].
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
|
|
||||||
-include("jsx_common.hrl").
|
-include("jsx_common.hrl").
|
||||||
|
-include("jsx_verify.hrl").
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,7 +39,17 @@
|
||||||
is_json(JSON, OptsList) when is_binary(JSON) ->
|
is_json(JSON, OptsList) when is_binary(JSON) ->
|
||||||
P = jsx:decoder(extract_parser_opts(OptsList)),
|
P = jsx:decoder(extract_parser_opts(OptsList)),
|
||||||
is_json(fun() -> P(JSON) end, OptsList);
|
is_json(fun() -> P(JSON) end, OptsList);
|
||||||
is_json(F, _OptsList) when is_function(F) -> collect(F(), [[]]).
|
is_json(F, OptsList) when is_function(F) ->
|
||||||
|
Opts = parse_opts(OptsList, #verify_opts{}),
|
||||||
|
case Opts#verify_opts.naked_values of
|
||||||
|
true -> collect(F(), Opts, [[]])
|
||||||
|
; false ->
|
||||||
|
case F() of
|
||||||
|
{event, start_object, Next} -> collect(Next(), Opts, [[]])
|
||||||
|
; {event, start_array, Next} -> collect(Next(), Opts, [[]])
|
||||||
|
; _ -> false
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
extract_parser_opts(Opts) ->
|
extract_parser_opts(Opts) ->
|
||||||
|
@ -57,36 +68,58 @@ extract_parser_opts([K|Rest], Acc) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
parse_opts([{repeated_keys, Val}|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#verify_opts{repeated_keys = Val});
|
||||||
|
parse_opts([repeated_keys|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#verify_opts{repeated_keys = true});
|
||||||
|
parse_opts([{naked_values, Val}|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#verify_opts{naked_values = Val});
|
||||||
|
parse_opts([naked_values|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts#verify_opts{naked_values = true});
|
||||||
|
parse_opts([_|Rest], Opts) ->
|
||||||
|
parse_opts(Rest, Opts);
|
||||||
|
parse_opts([], Opts) ->
|
||||||
|
Opts.
|
||||||
|
|
||||||
collect({event, end_json, _Next}, _Keys) ->
|
|
||||||
|
|
||||||
|
collect({event, end_json, _Next}, _Opts, _Keys) ->
|
||||||
true;
|
true;
|
||||||
|
|
||||||
|
|
||||||
%% allocate new key accumulator at start_object, discard it at end_object
|
%% allocate new key accumulator at start_object, discard it at end_object
|
||||||
collect({event, start_object, Next}, Keys) -> collect(Next(), [[]|Keys]);
|
collect({event, start_object, Next},
|
||||||
collect({event, end_object, Next}, [_|Keys]) -> collect(Next(), [Keys]);
|
Opts = #verify_opts{repeated_keys = false},
|
||||||
|
Keys) ->
|
||||||
|
collect(Next(), Opts, [[]|Keys]);
|
||||||
|
collect({event, end_object, Next},
|
||||||
|
Opts = #verify_opts{repeated_keys = false},
|
||||||
|
[_|Keys]) ->
|
||||||
|
collect(Next(), Opts, [Keys]);
|
||||||
|
|
||||||
|
|
||||||
%% check to see if key has already been encountered, if not add it to the key
|
%% check to see if key has already been encountered, if not add it to the key
|
||||||
%% accumulator and continue, else return false
|
%% accumulator and continue, else return false
|
||||||
collect({event, {key, Key}, Next}, [Current|Keys]) ->
|
collect({event, {key, Key}, Next},
|
||||||
|
Opts = #verify_opts{repeated_keys = false},
|
||||||
|
[Current|Keys]) ->
|
||||||
case lists:member(Key, Current) of
|
case lists:member(Key, Current) of
|
||||||
true -> false
|
true -> false
|
||||||
; false -> collect(Next(), [[Key] ++ Current] ++ Keys)
|
; false -> collect(Next(), Opts, [[Key] ++ Current] ++ Keys)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
collect({event, _, Next}, Keys) ->
|
collect({event, _, Next}, Opts, Keys) ->
|
||||||
collect(Next(), Keys);
|
collect(Next(), Opts, Keys);
|
||||||
|
|
||||||
|
|
||||||
%% needed to parse numbers that don't have trailing whitespace in less strict
|
%% needed to parse numbers that don't have trailing whitespace in less strict
|
||||||
%% mode
|
%% mode
|
||||||
collect({incomplete, More}, Keys) ->
|
collect({incomplete, More}, Opts, Keys) ->
|
||||||
collect(More(end_stream), Keys);
|
collect(More(end_stream), Opts, Keys);
|
||||||
|
|
||||||
|
|
||||||
collect(_, _) ->
|
collect(_, _, _) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
|
||||||
|
@ -144,13 +177,24 @@ false_test_() ->
|
||||||
{"unbalanced list", ?_assert(is_json(<<"[[[]]">>, []) =:= false)},
|
{"unbalanced list", ?_assert(is_json(<<"[[[]]">>, []) =:= false)},
|
||||||
{"trailing comma",
|
{"trailing comma",
|
||||||
?_assert(is_json(<<"[ true, false, null, ]">>, []) =:= false)
|
?_assert(is_json(<<"[ true, false, null, ]">>, []) =:= false)
|
||||||
},
|
}
|
||||||
{"repeated key",
|
].
|
||||||
|
|
||||||
|
repeated_keys_test_() ->
|
||||||
|
[
|
||||||
|
{"repeated key forbidden",
|
||||||
?_assert(is_json(
|
?_assert(is_json(
|
||||||
<<"{\"key\": true, \"key\": true}">>,
|
<<"{\"key\": true, \"key\": true}">>,
|
||||||
[]
|
[{repeated_keys, false}]
|
||||||
) =:= false
|
) =:= false
|
||||||
)
|
)
|
||||||
|
},
|
||||||
|
{"repeated key allowed",
|
||||||
|
?_assert(is_json(
|
||||||
|
<<"{\"key\": true, \"key\": true}">>,
|
||||||
|
[{repeated_keys, true}]
|
||||||
|
) =:= true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
||||||
|
@ -162,11 +206,20 @@ naked_value_test_() ->
|
||||||
{"naked number",
|
{"naked number",
|
||||||
?_assert(is_json(<<"1">>, []) =:= true)
|
?_assert(is_json(<<"1">>, []) =:= true)
|
||||||
},
|
},
|
||||||
|
{"naked string",
|
||||||
|
?_assert(is_json(<<"\"i am not json\"">>, []) =:= true)
|
||||||
|
},
|
||||||
|
{"naked true",
|
||||||
|
?_assert(is_json(<<"true">>, [{naked_values, false}]) =:= false)
|
||||||
|
},
|
||||||
|
{"naked number",
|
||||||
|
?_assert(is_json(<<"1">>, [{naked_values, false}]) =:= false)
|
||||||
|
},
|
||||||
{"naked string",
|
{"naked string",
|
||||||
?_assert(is_json(
|
?_assert(is_json(
|
||||||
<<"\"i am not json\"">>,
|
<<"\"i am not json\"">>,
|
||||||
[]
|
[{naked_values, false}]
|
||||||
) =:= true
|
) =:= false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
].
|
].
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue