From 89292c940d9d4040b07fde95cd8dcfd731313dbf Mon Sep 17 00:00:00 2001 From: alisdair sullivan Date: Wed, 4 Apr 2012 20:04:17 -0700 Subject: [PATCH] pre_encoders for preprocessing input to encoder --- src/jsx_encoder.erl | 121 ++++++++++++++++++++++++++++++++++++++++++-- src/jsx_opts.hrl | 3 +- src/jsx_utils.erl | 5 ++ 3 files changed, 125 insertions(+), 4 deletions(-) diff --git a/src/jsx_encoder.erl b/src/jsx_encoder.erl index 59c0343..7bf2b3c 100644 --- a/src/jsx_encoder.erl +++ b/src/jsx_encoder.erl @@ -49,7 +49,7 @@ encoder(Handler, State, Opts) -> start(Term, {Handler, State}, Opts) -> - Handler:handle_event(end_json, value(Term, {Handler, State}, Opts)). + Handler:handle_event(end_json, value(pre_encode(Term, Opts), {Handler, State}, Opts)). value(String, {Handler, State}, Opts) when is_binary(String) -> @@ -82,7 +82,7 @@ object([{Key, Value}|Rest], {Handler, State}, Opts) -> { Handler, value( - Value, + pre_encode(Value, Opts), {Handler, Handler:handle_event({key, clean_string(fix_key(Key), Opts)}, State)}, Opts ) @@ -94,11 +94,14 @@ object(Term, Handler, Opts) -> ?error([Term, Handler, Opts]). list([Value|Rest], {Handler, State}, Opts) -> - list(Rest, {Handler, value(Value, {Handler, State}, Opts)}, Opts); + list(Rest, {Handler, value(pre_encode(Value, Opts), {Handler, State}, Opts)}, Opts); list([], {Handler, State}, _Opts) -> Handler:handle_event(end_array, State); list(Term, Handler, Opts) -> ?error([Term, Handler, Opts]). +pre_encode(Value, Opts) -> lists:foldl(fun(F, V) -> F(V) end, Value, Opts#opts.pre_encoders). + + fix_key(Key) when is_atom(Key) -> fix_key(atom_to_binary(Key, utf8)); fix_key(Key) when is_binary(Key) -> Key. @@ -780,6 +783,118 @@ encode_test_() -> ]. +pre_encoders_test_() -> + Term = [ + {<<"object">>, [ + {<<"literals">>, [true, false, null]}, + {<<"strings">>, [<<"foo">>, <<"bar">>, <<"baz">>]}, + {<<"numbers">>, [1, 1.0, 1.0e0]} + ]} + ], + [ + {"no pre encode", ?_assertEqual( + encode(Term, []), + [ + start_object, + {key, <<"object">>}, start_object, + {key, <<"literals">>}, start_array, + {literal, true}, {literal, false}, {literal, null}, + end_array, + {key, <<"strings">>}, start_array, + {string, <<"foo">>}, {string, <<"bar">>}, {string, <<"baz">>}, + end_array, + {key, <<"numbers">>}, start_array, + {integer, 1}, {float, 1.0}, {float, 1.0}, + end_array, + end_object, + end_object, + end_json + ] + )}, + {"replace lists with empty lists", ?_assertEqual( + encode(Term, [{pre_encoders, [fun(V) -> case V of [{_,_}|_] -> V; [{}] -> V; V when is_list(V) -> []; _ -> V end end]}]), + [ + start_object, + {key, <<"object">>}, start_object, + {key, <<"literals">>}, start_array, end_array, + {key, <<"strings">>}, start_array, end_array, + {key, <<"numbers">>}, start_array, end_array, + end_object, + end_object, + end_json + ] + )}, + {"replace objects with empty objects", ?_assertEqual( + encode(Term, [{pre_encoders, [fun(V) -> case V of [{_,_}|_] -> [{}]; _ -> V end end]}]), + [ + start_object, + end_object, + end_json + ] + )}, + {"replace all non-list values with false", ?_assertEqual( + encode(Term, [{pre_encoders, [fun(V) when is_list(V) -> V; (_) -> false end]}]), + [ + start_object, + {key, <<"object">>}, start_object, + {key, <<"literals">>}, start_array, + {literal, false}, {literal, false}, {literal, false}, + end_array, + {key, <<"strings">>}, start_array, + {literal, false}, {literal, false}, {literal, false}, + end_array, + {key, <<"numbers">>}, start_array, + {literal, false}, {literal, false}, {literal, false}, + end_array, + end_object, + end_object, + end_json + ] + )}, + {"replace all atoms with atom_to_list", ?_assertEqual( + encode(Term, [{pre_encoders, [fun(V) when is_atom(V) -> unicode:characters_to_binary(atom_to_list(V)); (V) -> V end]}]), + [ + start_object, + {key, <<"object">>}, start_object, + {key, <<"literals">>}, start_array, + {string, <<"true">>}, {string, <<"false">>}, {string, <<"null">>}, + end_array, + {key, <<"strings">>}, start_array, + {string, <<"foo">>}, {string, <<"bar">>}, {string, <<"baz">>}, + end_array, + {key, <<"numbers">>}, start_array, + {integer, 1}, {float, 1.0}, {float, 1.0}, + end_array, + end_object, + end_object, + end_json + ] + )}, + {"replace all atoms to strings and back", ?_assertEqual( + encode(Term, [{pre_encoders, [ + fun(V) when is_atom(V) -> unicode:characters_to_binary(atom_to_list(V)); (V) -> V end, + fun(<<"true">>) -> true; (<<"false">>) -> false; (<<"null">>) -> null; (V) -> V end + ]}]), + [ + start_object, + {key, <<"object">>}, start_object, + {key, <<"literals">>}, start_array, + {literal, true}, {literal, false}, {literal, null}, + end_array, + {key, <<"strings">>}, start_array, + {string, <<"foo">>}, {string, <<"bar">>}, {string, <<"baz">>}, + end_array, + {key, <<"numbers">>}, start_array, + {integer, 1}, {float, 1.0}, {float, 1.0}, + end_array, + end_object, + end_object, + end_json + ] + )} + ]. + + escapes_test_() -> [ {"backspace escape", ?_assertEqual(encode(<<"\b">>, [escaped_strings]), [{string, <<"\\b">>}, end_json])}, diff --git a/src/jsx_opts.hrl b/src/jsx_opts.hrl index d62a790..625c737 100644 --- a/src/jsx_opts.hrl +++ b/src/jsx_opts.hrl @@ -7,5 +7,6 @@ escaped_strings = false, dirty_strings = false, ignored_bad_escapes = false, - explicit_end = false + explicit_end = false, + pre_encoders = [] }). \ No newline at end of file diff --git a/src/jsx_utils.erl b/src/jsx_utils.erl index c160c40..2afd8db 100644 --- a/src/jsx_utils.erl +++ b/src/jsx_utils.erl @@ -61,6 +61,10 @@ parse_opts([relax|Rest], Opts) -> comments = true, ignored_bad_escapes = true }); +parse_opts([{pre_encoders, Encoders}|Rest], Opts) when is_list(Encoders) -> + lists:foreach(fun(F) when is_function(F, 1) -> ok end, Encoders), + AllEncoders = Opts#opts.pre_encoders ++ Encoders, + parse_opts(Rest, Opts#opts{pre_encoders=AllEncoders}); %% deprecated flags parse_opts([loose_unicode|Rest], Opts) -> parse_opts(Rest, Opts#opts{replaced_bad_utf8=true}); @@ -90,6 +94,7 @@ valid_flags() -> ignored_bad_escapes, explicit_end, relax, + pre_encoders, %% deprecated flags loose_unicode, %% replaced_bad_utf8 escape_forward_slash, %% escaped_forward_slashes