Reputation:
I am new to Erlang
and noticed that there is no native function to create json string from lists (Or is there?). I use this method to create json string in Erlang but do not know if it will not malfunction.
Here is an example of my method:
-module(index).
-export([test/0]).
test() ->
Ma = "Hello World", Mb = "Hello Erlang",
A = "{\"Messages\" : [\"" ++ Ma ++ "\", \""++Mb++"\"], \"Usernames\" : [\"Username1\", \"Username2\"]}",
A.
The result is:
388> test().
"{\"Messages\" : [\"Hello World\", \"Hello Erlang\"], \"Usernames\" : [\"Username1\", \"Username2\"]}"
389>
I think this is the expected result but is there any chance that this method may malfunction when included special characters, such as: <, >, & / \ " ?
?
What precautions should I take to make this method stronger?
Upvotes: 3
Views: 2712
Reputation: 2704
I'm using jsone library.
%% Encode
> jsone:encode([1,2,3]).
<<"[1,2,3]">>
> jsone:encode(#{<<"key">> => <<"value">>}). % map format
> jsone:encode({[{<<"key">>, <<"value">>}]}). % tuple format
> jsone:encode([{<<"key">>, <<"value">>}]). % proplist format
<<"{\"key\":\"value\"}">>
> jsone:encode(#{key => <<"value">>}). % atom key is allowed
<<"{\"key\":\"value\"}">>
Upvotes: 0
Reputation: 1485
I had this very same problem, searched high and low and in the end came up with my own method. This is purely just pointing people in the right directions to finding a solution for themselves. Note: I tried jiffy but as I'm using rebar3 it's not currently compatible.
Im using MS sql server so i use the Erlang odbc module: http://erlang.org/doc/man/odbc.html
The odbc:sql_query/2 gives me back {selected, Columns, Results}
From here i can take the Columns
which is a list of strings & the Results, a list of rows represented each as a tuple, then create a few functions to output valid Erlang code to be able to serialize correctly to Json based on a number of factors. Here's the full code:
make the initial query:
Sql = "SELECT * FROM alloys;",
Ref = connect(),
case odbc:sql_query(Ref, Sql) of
{selected, Columns, Results} ->
set_json_from_sql(Columns, Results, []);
{error, Reason} ->
{error, Reason}
end.
Then the input function is set_json_from_sql/3
that calls the below functions:
format_by_type(Item) ->
if
is_list(Item) -> list_to_binary(io_lib:format("~s", [Item]));
is_integer(Item) -> Item;
is_boolean(Item) -> io_lib:format("~a", [Item]);
is_atom(Item) -> Item
end.
json_by_type([H], [Hc], Data) ->
NewH = format_by_type(H),
set_json_flatten(Data, Hc, NewH);
json_by_type([H|T], [Hc|Tc], Data) ->
NewH = format_by_type(H),
NewData = set_json_flatten(Data, Hc, NewH),
json_by_type(T, Tc, NewData).
set_json_flatten(Data, Column, Row) ->
ResTuple = {list_to_binary(Column), Row},
lists:flatten(Data, [ResTuple]).
set_json_from_sql([], [], Data) -> jsone:encode([{<<"data">>, lists:reverse(Data)}]);
set_json_from_sql(Columns, [H], Data) ->
NewData = set_json_merge(H, Columns, Data),
set_json_from_sql([], [], NewData);
set_json_from_sql(Columns, [H|T], Data) ->
NewData = set_json_merge(H, Columns, Data),
set_json_from_sql(Columns, T, NewData).
set_json_merge(Row, Columns, Data) ->
TupleRow = json_by_type(tuple_to_list(Row), Columns, []),
lists:append([TupleRow], Data).
So set_json_from_sql/3
gives you your Json output after matching set_json_from_sql([], [], Data)
.
The key points here are that you need to call list_to_binary/1
for strings & atoms. Use jsone
to encode Erlang objects to Json: https://github.com/sile/jsone
And, notice format_by_type/1
is used to match against Erlang object types, yes not ideal but works as long as you are aware of your DB's types or you can increase the extra guards to accommodate this.
Upvotes: 2
Reputation: 918
This works for me
test()->
Ma = "Hello World", Mb = "Hello Erlang",
A = "{\"Messages\" : {{\"Ma\":\"" ++ Ma ++ "\"}, {\"Mb\":\""++Mb++"\"}}, {\"Usernames\" : {\"Username1\":\"usrname1\"}, {\"Username2\":\"usrname2\"}}",
io:format("~s~n",[A]).
Output
10> io:format("~s~n",[A]).
{"Messages" : {{"Ma":Hello World"}, {"Mb":Hello Erlang"}}, {"Usernames" : {"Username1":"usrname1"}, {"Username2":"usrname2"}}
ok
or use one of many libraries on github to convert erlang terms to json. My Tuple to JSON module is simple but effective.
Upvotes: 0
Reputation: 51
Do it like a pro
-define(JSON_WRAPPER(Proplist), {Proplist}).
-spec from_list(json_proplist()) -> object().
from_list([]) -> new();
from_list(L) when is_list(L) -> ?JSON_WRAPPER(L).
-spec to_binary(atom() | string() | binary() | integer() | float() | pid() | iolist()) -> binary().
to_binary(X) when is_float(X) -> to_binary(mochinum:digits(X));
to_binary(X) when is_integer(X) -> list_to_binary(integer_to_list(X));
to_binary(X) when is_atom(X) -> list_to_binary(atom_to_list(X));
to_binary(X) when is_list(X) -> iolist_to_binary(X);
to_binary(X) when is_pid(X) -> to_binary(pid_to_list(X));
to_binary(X) when is_binary(X) -> X.
-spec recursive_from_proplist(any()) -> object().
recursive_from_proplist([]) -> new();
recursive_from_proplist(List) when is_list(List) ->
case lists:all(fun is_integer/1, List) of
'true' -> List;
'false' ->
from_list([{to_binary(K) ,recursive_from_proplist(V)}
|| {K,V} <- List
])
end;
recursive_from_proplist(Other) -> Other.
Upvotes: -1
Reputation: 1602
If Ma
or Mb
contains double quotes or whatever control characters, the parsing from string to JSON will fail. This parsing may never occur in Erlang, as Erlang does not have string to JSON conversion built-in.
It's a good idea to use binaries (<<"I am a binary string">>
), as lists consume a lot more resources.
We're using jiffy, which is implemented as a NIF and hence is reasonably fast and it allows for document construction like so:
jiffy:decode(<<"{\"foo\": \"bar\"}">>).
{[{<<"foo">>,<<"bar">>}]}
Doc = {[{foo, [<<"bing">>, 2.3, true]}]}.
{[{foo,[<<"bing">>,2.3,true]}]}
jiffy:encode(Doc).
<<"{\"foo\":[\"bing\",2.3,true]}">>
Upvotes: 2