[erlang-questions] Best way to send JSON from erlang to the browser and back
Joe Armstrong
erlang@REDACTED
Fri Nov 25 16:02:00 CET 2011
I have the following problem:
I have complex data structure that I want to send back and forward
between Erlang and a browser - what's the best way to do this?
This should be very simple - but the simplest solution I have found is
not entirely obvious. So I was wondering if this is the
best solution, and welcome any suggestions for improvement.
The problem is subject to the following constraints:
1) I want the data structure to be the same after it has been on
a "round-trip" from erlang -> browser -> erlang
2) I want to use publicly available libraries that are in-use and
maintained.
Here's the solution I use at the moment. I've chosen JSON for data
transport,
jquery in the browser, the Google chrome browser, and mochiweb.
(( is their a better choice - should I use something other than JSON for
the encoding, if so what,
Bert?, something else???)
Step 1
======
In the browser I request data from Erlang
I make a button in html with button id="b">click</button> and hook an event
onto it with
jquery:
$("#b").click(test_json);
function test_json(){
$.ajax({url:"get_term",
dataType:'json',
success:function(x){
handle(x);
}
});
};
When I click the button a "get_term" messages is sent to an erlang mochiweb
server.
Step 2
======
I make a mochiweb sevrer to reply, which looks like this:
start_server() ->
mochiweb_http:start([{name,?MODULE},
{loop,{?MODULE,loop}},
{port, 2234}]).
loop(Req) ->
handle(Req:get(path), Req).
handle("/get_term", Req) ->
X = [{struct,[{string,<<"joe">>},
{int,42},
{float,3.14159}]},
123,
<<"a string">>],
E = mochijson2:encode(X),
Req:ok({mochiweb_mime:from_extension(".js"),[],E});
The complex term is in the variable X. This is encoded with
mochijson2:encode(X)
Step 3
======
handle(x) is called in the browser - remember step 1? - handle is defined
like this:
function handle(x){
console.log('got',x);
$.ajax({url:'echo',
data:JSON.stringify(x),
success:function(y){
$("#ret").html(y);
}});
};
This prints the reply on the console and sends it back to Erlang.
To do this I call JSON.stringify(x). This seems to work correctly.
Step 4
======
Receive the reply in Erlang. I add an addition clause to handle/2,
handle("/echo", Req) ->
X = json_to_erl(Req),
io:format("~p~n",[X]),
...
where:
json_to_erl(Req) ->
[{Str,[]}] = Req:parse_qs(),
mochijson2:decode(Str).
This however is not quite right. The decoded version has changed
all the keys in the hashmap from atoms to binaries.
So when I send {struct, [{a,123}]} I get back {struct,[{<<"a">>,123}]}
This is easy to fix with:
munge({struct,L}) ->
{struct,[{list_to_atom(binary_to_list(I)), munge(J)} || {I,J}<-L]};
munge(L) when is_list(L) ->
[munge(I) || I <- L];
munge(X) -> X.
so the final version is:
json_to_erl(Req) ->
[{Str,[]}] = Req:parse_qs(),
munge(mochijson2:decode(Str)).
and amazingly the round trip succeeds. Why am I amazed? - the float
got reconstructed with full precision, and the order of the keys in
the hashmap was preserved.
To summarize:
1) mochijson2:encode(X) to term an erlang term to JSON
2) use the jquery function ajax to receive the term
called like this:
$.ajax({url:...,
dataType:'json',
success:function(x){ ...}
}).
3) use the jquery function ajax to send a javascript object x like this:
$.ajax({url:...
data:JSON.stringify(x),
...})
4) decode the object in erlang with (assuming Str is the return value
of
JSON.stringify(x))
munge(mochijson2:decode(Str))
So far my Erlang strings only contain simple english alphabetic characters,
so I don't really know what will happen if we get into utf8 encoded unicode
strings.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20111125/b7d3a57f/attachment.htm>
More information about the erlang-questions
mailing list