diff --git a/include/jsx_decoder.hrl b/include/jsx_decoder.hrl index 546d079..4baadcf 100644 --- a/include/jsx_decoder.hrl +++ b/include/jsx_decoder.hrl @@ -32,6 +32,7 @@ -record(opts, { comments = false, escaped_unicode = codepoint, + unquoted_keys = false, multi_term = false, encoding = auto }). @@ -143,6 +144,9 @@ parse_opts([{comments, Value}|Rest], Opts) -> parse_opts([{escaped_unicode, Value}|Rest], Opts) -> true = lists:member(Value, [ascii, codepoint, none]), parse_opts(Rest, Opts#opts{escaped_unicode=Value}); +parse_opts([{unquoted_keys, Value}|Rest], Opts) -> + true = lists:member(Value, [true, false]), + parse_opts(Rest, Opts#opts{unquoted_keys=Value}); parse_opts([{multi_term, Value}|Rest], Opts) -> true = lists:member(Value, [true, false]), parse_opts(Rest, Opts#opts{multi_term=Value}); @@ -247,6 +251,9 @@ object(<>, [key|Stack], Opts) -> {event, end_object, fun() -> maybe_done(Rest, Stack, Opts) end}; object(<>, Stack, #opts{comments=true}=Opts) -> maybe_comment(Rest, fun(Resume) -> object(Resume, Stack, Opts) end); +object(<>, Stack, #opts{unquoted_keys=true}=Opts) + when ?is_noncontrol(S) -> + unquoted_key(Rest, Stack, Opts, [S]); object(Bin, Stack, Opts) -> case ?partial_codepoint(Bin) of true -> @@ -353,6 +360,9 @@ key(<>, Stack, Opts) -> string(Rest, Stack, Opts, []); key(<>, Stack, #opts{comments=true}=Opts) -> maybe_comment(Rest, fun(Resume) -> key(Resume, Stack, Opts) end); +key(<>, Stack, #opts{unquoted_keys=true}=Opts) + when ?is_noncontrol(S) -> + unquoted_key(Rest, Stack, Opts, [S]); key(Bin, Stack, Opts) -> case ?partial_codepoint(Bin) of true -> @@ -365,6 +375,33 @@ key(Bin, Stack, Opts) -> end. +unquoted_key(<>, Stack, Opts, Acc) + when ?is_whitespace(S) -> + {event, {key, lists:reverse(Acc)}, fun() -> colon(Rest, Stack, Opts) end}; +unquoted_key(<>, [key|Stack], Opts, Acc) -> + {event, + {key, lists:reverse(Acc)}, + fun() -> value(Rest, [object|Stack], Opts) end + }; +unquoted_key(<>, Stack, Opts, Acc) + when ?is_noncontrol(S) -> + unquoted_key(Rest, Stack, Opts, [S] ++ Acc); +unquoted_key(Bin, Stack, Opts, Acc) -> + case partial_utf(Bin) of + true -> + {incomplete, fun(end_stream) -> + {error, badjson} + ; (Stream) -> + unquoted_key(<>, + Stack, + Opts, + Acc + ) + end} + ; false -> {error, badjson} + end. + + %% string has an additional parameter, an accumulator (Acc) used to hold the %% intermediate representation of the string being parsed. using a list of %% integers representing unicode codepoints is faster than constructing diff --git a/test/cases/object_with_unquoted_keys.json b/test/cases/object_with_unquoted_keys.json new file mode 100644 index 0000000..c4b564a --- /dev/null +++ b/test/cases/object_with_unquoted_keys.json @@ -0,0 +1 @@ +{foo : "bar", baz:true, false : null,object:{ key : "value" },list:[null,null,null,[],"\n\r\\"]} \ No newline at end of file diff --git a/test/cases/object_with_unquoted_keys.test b/test/cases/object_with_unquoted_keys.test new file mode 100644 index 0000000..f15b287 --- /dev/null +++ b/test/cases/object_with_unquoted_keys.test @@ -0,0 +1,23 @@ +{name, "object with unquoted keys"}. +{jsx, [start_object, + {key,"foo"}, + {string,"bar"}, + {key,"baz"}, + {literal,true}, + {key,"false"}, + {literal,null}, + {key,"object"}, + start_object, + {key,"key"}, + {string,"value"}, + end_object, + {key,"list"}, + start_array, + {literal,null}, + {literal,null}, + {literal,null}, + start_array,end_array, + {string,"\n\r\\"}, + end_array,end_object,end_json]}. +{json, "object_with_unquoted_keys.json"}. +{jsx_flags, [{unquoted_keys, true}]}.