blob: 72bb6c15701e7cf76ef9b54c4c7e8b5997fed104 [file] [log] [blame]
% Licensed under the Apache License, Version 2.0 (the "License"); you may not
% use this file except in compliance with the License. You may obtain a copy of
% the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
% License for the specific language governing permissions and limitations under
% the License.
-module(ejson).
-export([encode/1, decode/1]).
-on_load(init/0).
init() ->
SoName = case code:priv_dir(ejson) of
{error, bad_name} ->
case filelib:is_dir(filename:join(["..", priv])) of
true ->
filename:join(["..", priv, ejson]);
false ->
filename:join([priv, ejson])
end;
Dir ->
filename:join(Dir, ejson)
end,
(catch erlang:load_nif(SoName, 0)),
case erlang:system_info(otp_release) of
"R13B03" -> true;
_ -> ok
end.
decode(undefined) ->
throw({invalid_json, undefined});
decode(IoList) ->
try
nif_decode(IoList)
catch exit:ejson_nif_not_loaded ->
erl_decode(IoList)
end.
encode(EJson) ->
try
nif_encode(EJson)
catch exit:ejson_nif_not_loaded ->
erl_encode(EJson)
end.
nif_decode(IoList) ->
case reverse_tokens(IoList) of
{ok, ReverseTokens} ->
[[EJson]] = make_ejson(ReverseTokens, [[]]),
EJson;
Error ->
throw({invalid_json, {Error, IoList}})
end.
erl_decode(IoList) ->
try
(mochijson2:decoder([{object_hook, fun({struct, L}) -> {L} end}]))(IoList)
catch _Type:Error ->
throw({invalid_json, {Error, IoList}})
end.
nif_encode(EJson) ->
RevList = encode_rev(EJson),
final_encode(lists:reverse(lists:flatten([RevList]))).
erl_encode(EJson) ->
Opts = [{handler, fun mochi_encode_handler/1}],
iolist_to_binary((mochijson2:encoder(Opts))(EJson)).
mochi_encode_handler({L}) when is_list(L) ->
{struct, L};
mochi_encode_handler(Bad) ->
exit({json_encode, {bad_term, Bad}}).
% Encode the json into a reverse list that's almost an iolist
% everything in the list is the final output except for tuples with
% {0, Strings} and {1, Floats}, which are to be converted to strings
% inside the NIF.
encode_rev(true) ->
<<"true">>;
encode_rev(false) ->
<<"false">>;
encode_rev(null) ->
<<"null">>;
encode_rev(I) when is_integer(I) ->
list_to_binary(integer_to_list(I));
encode_rev(S) when is_binary(S) ->
{0, S};
encode_rev(S) when is_atom(S) ->
{0, list_to_binary(atom_to_list(S))};
encode_rev(F) when is_float(F) ->
{1, F};
encode_rev({Props}) when is_list(Props) ->
encode_proplist_rev(Props, [<<"{">>]);
encode_rev(Array) when is_list(Array) ->
encode_array_rev(Array, [<<"[">>]);
encode_rev(Bad) ->
throw({json_encode, {bad_term, Bad}}).
encode_array_rev([], Acc) ->
[<<"]">> | Acc];
encode_array_rev([Val | Rest], [<<"[">>]) ->
encode_array_rev(Rest, [encode_rev(Val), <<"[">>]);
encode_array_rev([Val | Rest], Acc) ->
encode_array_rev(Rest, [encode_rev(Val), <<",">> | Acc]).
encode_proplist_rev([], Acc) ->
[<<"}">> | Acc];
encode_proplist_rev([{Key,Val} | Rest], [<<"{">>]) ->
encode_proplist_rev(
Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<"{">>]);
encode_proplist_rev([{Key,Val} | Rest], Acc) ->
encode_proplist_rev(
Rest, [encode_rev(Val), <<":">>, {0, as_binary(Key)}, <<",">> | Acc]).
as_binary(B) when is_binary(B) ->
B;
as_binary(A) when is_atom(A) ->
list_to_binary(atom_to_list(A));
as_binary(L) when is_list(L) ->
list_to_binary(L).
make_ejson([], Stack) ->
Stack;
make_ejson([0 | RevEvs], [ArrayValues, PrevValues | RestStack]) ->
% 0 ArrayStart
make_ejson(RevEvs, [[ArrayValues | PrevValues] | RestStack]);
make_ejson([1 | RevEvs], Stack) ->
% 1 ArrayEnd
make_ejson(RevEvs, [[] | Stack]);
make_ejson([2 | RevEvs], [ObjValues, PrevValues | RestStack]) ->
% 2 ObjectStart
make_ejson(RevEvs, [[{ObjValues} | PrevValues] | RestStack]);
make_ejson([3 | RevEvs], Stack) ->
% 3 ObjectEnd
make_ejson(RevEvs, [[] | Stack]);
make_ejson([{0, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
% {0, IntegerString}
make_ejson(RevEvs, [[list_to_integer(binary_to_list(Value)) | Vals] | RestStack]);
make_ejson([{1, Value} | RevEvs], [Vals | RestStack] = _Stack) ->
% {1, FloatString}
make_ejson(RevEvs, [[list_to_float(binary_to_list(Value)) | Vals] | RestStack]);
make_ejson([{3, String} | RevEvs], [[PrevValue|RestObject] | RestStack] = _Stack) ->
% {3 , ObjectKey}
make_ejson(RevEvs, [[{String, PrevValue}|RestObject] | RestStack]);
make_ejson([Value | RevEvs], [Vals | RestStack] = _Stack) ->
make_ejson(RevEvs, [[Value | Vals] | RestStack]).
reverse_tokens(_) ->
exit(ejson_nif_not_loaded).
final_encode(_) ->
exit(ejson_nif_not_loaded).