unifies returns of encoder and decoder

This commit is contained in:
alisdair sullivan 2011-08-15 17:26:11 -07:00
parent 48ada362e9
commit d33474d29d

View file

@ -74,8 +74,11 @@ emit([Event|Events], {State, Rest, T, Args}) ->
bad_json(Stream, _) -> {error, {badjson, Stream}}. bad_json(Stream, _) -> {error, {badjson, Stream}}.
start({string, String}, T, Opts) when is_binary(String) -> start({string, String}, T, Opts) when is_binary(String); is_list(String) ->
emit([{string, json_escape(String, Opts)}, end_json, incomplete], emit([{string, unicode:characters_to_list(json_escape(String, Opts))},
end_json,
incomplete
],
{bad_json, [], T, []} {bad_json, [], T, []}
); );
start({float, Float}, T, _Opts) when is_float(Float) -> start({float, Float}, T, _Opts) when is_float(Float) ->
@ -97,8 +100,10 @@ list_or_object([], T, Stack, Opts) ->
list_or_object(Forms, _, _, _) -> {error, {badjson, Forms}}. list_or_object(Forms, _, _, _) -> {error, {badjson, Forms}}.
key([{key, Key}|Forms], T, Stack, Opts) when is_binary(Key) -> key([{key, Key}|Forms], T, Stack, Opts) when is_binary(Key); is_list(Key) ->
emit([{key, json_escape(Key, Opts)}], {value, Forms, T, [Stack, Opts]}); emit([{key, unicode:characters_to_list(json_escape(Key, Opts))}],
{value, Forms, T, [Stack, Opts]}
);
key([end_object|Forms], T, [object|Stack], Opts) -> key([end_object|Forms], T, [object|Stack], Opts) ->
emit([end_object], {maybe_done, Forms, T, [Stack, Opts]}); emit([end_object], {maybe_done, Forms, T, [Stack, Opts]});
key([], T, Stack, Opts) -> key([], T, Stack, Opts) ->
@ -106,8 +111,8 @@ key([], T, Stack, Opts) ->
key(Forms, _, _, _) -> {error, {badjson, Forms}}. key(Forms, _, _, _) -> {error, {badjson, Forms}}.
value([{string, S}|Forms], T, Stack, Opts) when is_binary(S) -> value([{string, S}|Forms], T, Stack, Opts) when is_binary(S); is_list(S) ->
emit([{string, json_escape(S, Opts)}], emit([{string, unicode:characters_to_list(json_escape(S, Opts))}],
{maybe_done, Forms, T, [Stack, Opts]} {maybe_done, Forms, T, [Stack, Opts]}
); );
value([{float, F}|Forms], T, Stack, Opts) when is_float(F) -> value([{float, F}|Forms], T, Stack, Opts) when is_float(F) ->
@ -147,37 +152,59 @@ maybe_done(Forms, _, _, _) -> {error, {badjson, Forms}}.
%% json string escaping, for utf8 binaries. escape the json control sequences to %% json string escaping, for utf8 binaries. escape the json control sequences to
%% their json equivalent, escape other control characters to \uXXXX sequences, %% their json equivalent, escape other control characters to \uXXXX sequences,
%% everything else should be a legal json string component %% everything else should be a legal json string component
json_escape(String, Opts) -> json_escape(String, Opts) when is_binary(String) ->
json_escape(String, Opts, <<>>). json_escape(String, Opts, <<>>);
json_escape(String, Opts) when is_list(String) ->
json_escape(String, Opts, []).
%% double quote %% double quote
json_escape(<<$\", Rest/binary>>, Opts, Acc) -> json_escape(<<$\", Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $\">>); json_escape(Rest, Opts, <<Acc/binary, $\\, $\">>);
json_escape([$\"|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$\", $\\] ++ Acc);
%% backslash \ reverse solidus %% backslash \ reverse solidus
json_escape(<<$\\, Rest/binary>>, Opts, Acc) -> json_escape(<<$\\, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $\\>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $\\>>);
json_escape([$\\|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$\\, $\\] ++ Acc);
%% backspace %% backspace
json_escape(<<$\b, Rest/binary>>, Opts, Acc) -> json_escape(<<$\b, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $b>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $b>>);
json_escape([$\b|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$b, $\\] ++ Acc);
%% form feed %% form feed
json_escape(<<$\f, Rest/binary>>, Opts, Acc) -> json_escape(<<$\f, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $f>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $f>>);
json_escape([$\f|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$f, $\\] ++ Acc);
%% newline %% newline
json_escape(<<$\n, Rest/binary>>, Opts, Acc) -> json_escape(<<$\n, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $n>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $n>>);
json_escape([$\n|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$n, $\\] ++ Acc);
%% cr %% cr
json_escape(<<$\r, Rest/binary>>, Opts, Acc) -> json_escape(<<$\r, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $r>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $r>>);
json_escape([$\r|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$r, $\\] ++ Acc);
%% tab %% tab
json_escape(<<$\t, Rest/binary>>, Opts, Acc) -> json_escape(<<$\t, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $t>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $t>>);
json_escape([$\t|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [$t, $\\] ++ Acc);
%% other control characters %% other control characters
json_escape(<<C/utf8, Rest/binary>>, Opts, Acc) when C >= 0, C < $\s -> json_escape(<<C/utf8, Rest/binary>>, Opts, Acc) when C >= 0, C < $\s ->
json_escape(Rest, Opts, <<Acc/binary, (json_escape_sequence(C))/binary>>); json_escape(Rest, Opts, <<Acc/binary, (json_escape_sequence(C))/binary>>);
json_escape([C|Rest], Opts, Acc) when C >= 0, C < $\s ->
json_escape(Rest, Opts,
lists:reverse(unicode:characters_to_list(json_escape_sequence(C)))
++ [Acc]);
%% escape forward slashes -- optionally -- to faciliate microsoft's retarded %% escape forward slashes -- optionally -- to faciliate microsoft's retarded
%% date format %% date format
json_escape(<<$/, Rest/binary>>, Opts=#opts{escape_forward_slash=true}, Acc) -> json_escape(<<$/, Rest/binary>>, Opts=#opts{escape_forward_slash=true}, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, $\\, $/>>); json_escape(Rest, Opts, <<Acc/binary, $\\, $/>>);
json_escape([$/|Rest], Opts=#opts{escape_forward_slash=true}, Acc) ->
json_escape(Rest, Opts, [$/, $\\] ++ Acc);
%% escape u+2028 and u+2029 to avoid problems with jsonp %% escape u+2028 and u+2029 to avoid problems with jsonp
json_escape(<<C/utf8, Rest/binary>>, Opts, Acc) json_escape(<<C/utf8, Rest/binary>>, Opts, Acc)
when C == 16#2028; C == 16#2029 -> when C == 16#2028; C == 16#2029 ->
@ -185,8 +212,12 @@ json_escape(<<C/utf8, Rest/binary>>, Opts, Acc)
%% any other legal codepoint %% any other legal codepoint
json_escape(<<C/utf8, Rest/binary>>, Opts, Acc) -> json_escape(<<C/utf8, Rest/binary>>, Opts, Acc) ->
json_escape(Rest, Opts, <<Acc/binary, C/utf8>>); json_escape(Rest, Opts, <<Acc/binary, C/utf8>>);
json_escape([C|Rest], Opts, Acc) ->
json_escape(Rest, Opts, [C] ++ Acc);
json_escape(<<>>, _Opts, Acc) -> json_escape(<<>>, _Opts, Acc) ->
Acc; Acc;
json_escape([], _Opts, Acc) ->
lists:reverse(Acc);
json_escape(_, _, _) -> json_escape(_, _, _) ->
erlang:error(badarg). erlang:error(badarg).
@ -251,9 +282,9 @@ encode_test_() ->
{"empty object", ?_assert(encode([start_object, end_object, end_json]))}, {"empty object", ?_assert(encode([start_object, end_object, end_json]))},
{"empty array", ?_assert(encode([start_array, end_array, end_json]))}, {"empty array", ?_assert(encode([start_array, end_array, end_json]))},
{"nested empty objects", ?_assert(encode([start_object, {"nested empty objects", ?_assert(encode([start_object,
{key, <<"empty object">>}, {key, "empty object"},
start_object, start_object,
{key, <<"empty object">>}, {key, "empty object"},
start_object, start_object,
end_object, end_object,
end_object, end_object,
@ -269,19 +300,19 @@ encode_test_() ->
end_json end_json
]))}, ]))},
{"simple object", ?_assert(encode([start_object, {"simple object", ?_assert(encode([start_object,
{key, <<"a">>}, {key, "a"},
{string, <<"hello">>}, {string, "hello"},
{key, <<"b">>}, {key, "b"},
{integer, 1}, {integer, 1},
{key, <<"c">>}, {key, "c"},
{float, 1.0}, {float, 1.0},
{key, <<"d">>}, {key, "d"},
{literal, true}, {literal, true},
end_object, end_object,
end_json end_json
]))}, ]))},
{"simple array", ?_assert(encode([start_array, {"simple array", ?_assert(encode([start_array,
{string, <<"hello">>}, {string, "hello"},
{integer, 1}, {integer, 1},
{float, 1.0}, {float, 1.0},
{literal, true}, {literal, true},
@ -293,7 +324,7 @@ encode_test_() ->
end_array, end_array,
end_json end_json
]))}, ]))},
{"naked string", ?_assert(encode({string, <<"hello">>}))}, {"naked string", ?_assert(encode({string, "hello"}))},
{"naked literal", ?_assert(encode({literal, true}))}, {"naked literal", ?_assert(encode({literal, true}))},
{"naked integer", ?_assert(encode({integer, 1}))}, {"naked integer", ?_assert(encode({integer, 1}))},
{"naked float", ?_assert(encode({float, 1.0}))} {"naked float", ?_assert(encode({float, 1.0}))}