Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 568 lines (510 sloc) 18.943 kB
4842019 @mdempsky first post
mdempsky authored
1 %% @author Bob Ippolito <bob@mochimedia.com>
2 %% @copyright 2006 Mochi Media, Inc.
3
4 %% @doc Yet another JSON (RFC 4627) library for Erlang.
5
6 -module(mochijson).
7 -author('bob@mochimedia.com').
8 -export([encoder/1, encode/1]).
9 -export([decoder/1, decode/1]).
09c9099 @etrepum bring mochijson2 API into mochijson as binary_*
etrepum authored
10 -export([binary_encoder/1, binary_encode/1]).
11 -export([binary_decoder/1, binary_decode/1]).
4842019 @mdempsky first post
mdempsky authored
12 -export([test/0]).
13
14 % This is a macro to placate syntax highlighters..
15 -define(Q, $\").
16 -define(ADV_COL(S, N), S#decoder{column=N+S#decoder.column}).
17 -define(INC_COL(S), S#decoder{column=1+S#decoder.column}).
18 -define(INC_LINE(S), S#decoder{column=1, line=1+S#decoder.line}).
19
20 %% @type iolist() = [char() | binary() | iolist()]
21 %% @type iodata() = iolist() | binary()
22 %% @type json_string() = atom | string() | binary()
23 %% @type json_number() = integer() | float()
24 %% @type json_array() = {array, [json_term()]}
25 %% @type json_object() = {struct, [{json_string(), json_term()}]}
26 %% @type json_term() = json_string() | json_number() | json_array() |
27 %% json_object()
28 %% @type encoding() = utf8 | unicode
29 %% @type encoder_option() = {input_encoding, encoding()} |
30 %% {handler, function()}
31 %% @type decoder_option() = {input_encoding, encoding()} |
32 %% {object_hook, function()}
09c9099 @etrepum bring mochijson2 API into mochijson as binary_*
etrepum authored
33 %% @type bjson_string() = binary()
34 %% @type bjson_number() = integer() | float()
35 %% @type bjson_array() = [bjson_term()]
36 %% @type bjson_object() = {struct, [{bjson_string(), bjson_term()}]}
37 %% @type bjson_term() = bjson_string() | bjson_number() | bjson_array() |
38 %% bjson_object()
39 %% @type binary_encoder_option() = {handler, function()}
40 %% @type binary_decoder_option() = {object_hook, function()}
4842019 @mdempsky first post
mdempsky authored
41
42 -record(encoder, {input_encoding=unicode,
43 handler=null}).
44
45 -record(decoder, {input_encoding=utf8,
46 object_hook=null,
47 line=1,
48 column=1,
49 state=null}).
50
51 %% @spec encoder([encoder_option()]) -> function()
52 %% @doc Create an encoder/1 with the given options.
53 encoder(Options) ->
54 State = parse_encoder_options(Options, #encoder{}),
55 fun (O) -> json_encode(O, State) end.
56
57 %% @spec encode(json_term()) -> iolist()
58 %% @doc Encode the given as JSON to an iolist.
59 encode(Any) ->
60 json_encode(Any, #encoder{}).
61
62 %% @spec decoder([decoder_option()]) -> function()
63 %% @doc Create a decoder/1 with the given options.
64 decoder(Options) ->
65 State = parse_decoder_options(Options, #decoder{}),
66 fun (O) -> json_decode(O, State) end.
67
68 %% @spec decode(iolist()) -> json_term()
69 %% @doc Decode the given iolist to Erlang terms.
70 decode(S) ->
71 json_decode(S, #decoder{}).
72
09c9099 @etrepum bring mochijson2 API into mochijson as binary_*
etrepum authored
73 %% @spec binary_decoder([binary_decoder_option()]) -> function()
74 %% @doc Create a binary_decoder/1 with the given options.
75 binary_decoder(Options) ->
76 mochijson2:decoder(Options).
77
78 %% @spec binary_encoder([binary_encoder_option()]) -> function()
79 %% @doc Create a binary_encoder/1 with the given options.
80 binary_encoder(Options) ->
81 mochijson2:encoder(Options).
82
83 %% @spec binary_encode(bjson_term()) -> iolist()
84 %% @doc Encode the given as JSON to an iolist, using lists for arrays and
85 %% binaries for strings.
86 binary_encode(Any) ->
87 mochijson2:encode(Any).
88
89 %% @spec binary_decode(iolist()) -> bjson_term()
90 %% @doc Decode the given iolist to Erlang terms, using lists for arrays and
91 %% binaries for strings.
92 binary_decode(S) ->
93 mochijson2:decode(S).
94
4842019 @mdempsky first post
mdempsky authored
95 test() ->
09c9099 @etrepum bring mochijson2 API into mochijson as binary_*
etrepum authored
96 test_all(),
97 mochijson2:test().
4842019 @mdempsky first post
mdempsky authored
98
99 %% Internal API
100
101 parse_encoder_options([], State) ->
102 State;
103 parse_encoder_options([{input_encoding, Encoding} | Rest], State) ->
104 parse_encoder_options(Rest, State#encoder{input_encoding=Encoding});
105 parse_encoder_options([{handler, Handler} | Rest], State) ->
106 parse_encoder_options(Rest, State#encoder{handler=Handler}).
107
108 parse_decoder_options([], State) ->
109 State;
110 parse_decoder_options([{input_encoding, Encoding} | Rest], State) ->
111 parse_decoder_options(Rest, State#decoder{input_encoding=Encoding});
112 parse_decoder_options([{object_hook, Hook} | Rest], State) ->
113 parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
114
115
116 format_float(F) ->
117 format_float1(lists:reverse(float_to_list(F)), []).
118
119 format_float1([$0, $0, _, $e | Rest], []) ->
120 strip_zeros(Rest, []);
121 format_float1([Sign, $e | Rest], Acc) ->
122 strip_zeros(Rest, [$e, Sign | Acc]);
123 format_float1([C | Rest], Acc) ->
124 format_float1(Rest, [C | Acc]).
125
126 strip_zeros(L=[$0, $. | _], Acc) ->
127 lists:reverse(L, Acc);
128 strip_zeros([$0 | Rest], Acc) ->
129 strip_zeros(Rest, Acc);
130 strip_zeros(L, Acc) ->
131 lists:reverse(L, Acc).
132
133 json_encode(true, _State) ->
134 "true";
135 json_encode(false, _State) ->
136 "false";
137 json_encode(null, _State) ->
138 "null";
139 json_encode(I, _State) when is_integer(I) ->
140 integer_to_list(I);
141 json_encode(F, _State) when is_float(F) ->
142 format_float(F);
143 json_encode(L, State) when is_list(L); is_binary(L); is_atom(L) ->
144 json_encode_string(L, State);
145 json_encode({array, Props}, State) when is_list(Props) ->
146 json_encode_array(Props, State);
147 json_encode({struct, Props}, State) when is_list(Props) ->
148 json_encode_proplist(Props, State);
149 json_encode(Bad, #encoder{handler=null}) ->
150 exit({json_encode, {bad_term, Bad}});
151 json_encode(Bad, State=#encoder{handler=Handler}) ->
152 json_encode(Handler(Bad), State).
153
154 json_encode_array([], _State) ->
155 "[]";
156 json_encode_array(L, State) ->
157 F = fun (O, Acc) ->
158 [$,, json_encode(O, State) | Acc]
159 end,
160 [$, | Acc1] = lists:foldl(F, "[", L),
161 lists:reverse([$\] | Acc1]).
162
163 json_encode_proplist([], _State) ->
164 "{}";
165 json_encode_proplist(Props, State) ->
166 F = fun ({K, V}, Acc) ->
167 KS = case K of
168 K when is_atom(K) ->
169 json_encode_string_utf8(atom_to_list(K), [?Q]);
170 K when is_integer(K) ->
171 json_encode_string(integer_to_list(K), State);
172 K when is_list(K); is_binary(K) ->
173 json_encode_string(K, State)
174 end,
175 VS = json_encode(V, State),
176 [$,, VS, $:, KS | Acc]
177 end,
178 [$, | Acc1] = lists:foldl(F, "{", Props),
179 lists:reverse([$\} | Acc1]).
180
181 json_encode_string(A, _State) when is_atom(A) ->
182 json_encode_string_unicode(xmerl_ucs:from_utf8(atom_to_list(A)), [?Q]);
183 json_encode_string(B, _State) when is_binary(B) ->
184 json_encode_string_unicode(xmerl_ucs:from_utf8(B), [?Q]);
185 json_encode_string(S, #encoder{input_encoding=utf8}) ->
186 json_encode_string_utf8(S, [?Q]);
187 json_encode_string(S, #encoder{input_encoding=unicode}) ->
188 json_encode_string_unicode(S, [?Q]).
189
190 json_encode_string_utf8([], Acc) ->
191 lists:reverse([$\" | Acc]);
192 json_encode_string_utf8(All=[C | Cs], Acc) ->
193 case C of
194 C when C >= 16#7f ->
195 json_encode_string_unicode(xmerl_ucs:from_utf8(All), Acc);
196 _ ->
197 Acc1 = case C of
198 ?Q ->
199 [?Q, $\\ | Acc];
200 $/ ->
201 [$/, $\\ | Acc];
202 $\\ ->
203 [$\\, $\\ | Acc];
204 $\b ->
205 [$b, $\\ | Acc];
206 $\f ->
207 [$f, $\\ | Acc];
208 $\n ->
209 [$n, $\\ | Acc];
210 $\r ->
211 [$r, $\\ | Acc];
212 $\t ->
213 [$t, $\\ | Acc];
214 C when C >= 0, C < $\s ->
215 [unihex(C) | Acc];
216 C when C >= $\s ->
217 [C | Acc];
218 _ ->
219 exit({json_encode, {bad_char, C}})
220 end,
221 json_encode_string_utf8(Cs, Acc1)
222 end.
223
224 json_encode_string_unicode([], Acc) ->
225 lists:reverse([$\" | Acc]);
226 json_encode_string_unicode([C | Cs], Acc) ->
227 Acc1 = case C of
228 ?Q ->
229 [?Q, $\\ | Acc];
230 $/ ->
231 [$/, $\\ | Acc];
232 $\\ ->
233 [$\\, $\\ | Acc];
234 $\b ->
235 [$b, $\\ | Acc];
236 $\f ->
237 [$f, $\\ | Acc];
238 $\n ->
239 [$n, $\\ | Acc];
240 $\r ->
241 [$r, $\\ | Acc];
242 $\t ->
243 [$t, $\\ | Acc];
244 C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF ->
245 [unihex(C) | Acc];
246 C when C < 16#7f ->
247 [C | Acc];
248 _ ->
249 exit({json_encode, {bad_char, C}})
250 end,
251 json_encode_string_unicode(Cs, Acc1).
252
253 dehex(C) when C >= $0, C =< $9 ->
254 C - $0;
255 dehex(C) when C >= $a, C =< $f ->
256 C - $a + 10;
257 dehex(C) when C >= $A, C =< $F ->
258 C - $A + 10.
259
260 hexdigit(C) when C >= 0, C =< 9 ->
261 C + $0;
262 hexdigit(C) when C =< 15 ->
263 C + $a - 10.
264
265 unihex(C) when C < 16#10000 ->
266 <<D3:4, D2:4, D1:4, D0:4>> = <<C:16>>,
267 Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]],
268 [$\\, $u | Digits];
269 unihex(C) when C =< 16#10FFFF ->
270 N = C - 16#10000,
271 S1 = 16#d800 bor ((N bsr 10) band 16#3ff),
272 S2 = 16#dc00 bor (N band 16#3ff),
273 [unihex(S1), unihex(S2)].
274
275 json_decode(B, S) when is_binary(B) ->
276 json_decode([B], S);
277 json_decode(L, S) ->
278 {Res, L1, S1} = decode1(L, S),
279 {eof, [], _} = tokenize(L1, S1#decoder{state=trim}),
280 Res.
281
282 decode1(L, S=#decoder{state=null}) ->
283 case tokenize(L, S#decoder{state=any}) of
284 {{const, C}, L1, S1} ->
285 {C, L1, S1};
286 {start_array, L1, S1} ->
287 decode_array(L1, S1#decoder{state=any}, []);
288 {start_object, L1, S1} ->
289 decode_object(L1, S1#decoder{state=key}, [])
290 end.
291
292 make_object(V, #decoder{object_hook=null}) ->
293 V;
294 make_object(V, #decoder{object_hook=Hook}) ->
295 Hook(V).
296
297 decode_object(L, S=#decoder{state=key}, Acc) ->
298 case tokenize(L, S) of
299 {end_object, Rest, S1} ->
300 V = make_object({struct, lists:reverse(Acc)}, S1),
301 {V, Rest, S1#decoder{state=null}};
302 {{const, K}, Rest, S1} when is_list(K) ->
303 {colon, L2, S2} = tokenize(Rest, S1),
304 {V, L3, S3} = decode1(L2, S2#decoder{state=null}),
305 decode_object(L3, S3#decoder{state=comma}, [{K, V} | Acc])
306 end;
307 decode_object(L, S=#decoder{state=comma}, Acc) ->
308 case tokenize(L, S) of
309 {end_object, Rest, S1} ->
310 V = make_object({struct, lists:reverse(Acc)}, S1),
311 {V, Rest, S1#decoder{state=null}};
312 {comma, Rest, S1} ->
313 decode_object(Rest, S1#decoder{state=key}, Acc)
314 end.
315
316 decode_array(L, S=#decoder{state=any}, Acc) ->
317 case tokenize(L, S) of
318 {end_array, Rest, S1} ->
319 {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
320 {start_array, Rest, S1} ->
321 {Array, Rest1, S2} = decode_array(Rest, S1#decoder{state=any}, []),
322 decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
323 {start_object, Rest, S1} ->
324 {Array, Rest1, S2} = decode_object(Rest, S1#decoder{state=key}, []),
325 decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]);
326 {{const, Const}, Rest, S1} ->
327 decode_array(Rest, S1#decoder{state=comma}, [Const | Acc])
328 end;
329 decode_array(L, S=#decoder{state=comma}, Acc) ->
330 case tokenize(L, S) of
331 {end_array, Rest, S1} ->
332 {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}};
333 {comma, Rest, S1} ->
334 decode_array(Rest, S1#decoder{state=any}, Acc)
335 end.
336
337 tokenize_string(IoList=[C | _], S=#decoder{input_encoding=utf8}, Acc)
338 when is_list(C); is_binary(C); C >= 16#7f ->
339 List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
340 tokenize_string(List, S#decoder{input_encoding=unicode}, Acc);
341 tokenize_string("\"" ++ Rest, S, Acc) ->
342 {lists:reverse(Acc), Rest, ?INC_COL(S)};
343 tokenize_string("\\\"" ++ Rest, S, Acc) ->
344 tokenize_string(Rest, ?ADV_COL(S, 2), [$\" | Acc]);
345 tokenize_string("\\\\" ++ Rest, S, Acc) ->
346 tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]);
347 tokenize_string("\\/" ++ Rest, S, Acc) ->
348 tokenize_string(Rest, ?ADV_COL(S, 2), [$/ | Acc]);
349 tokenize_string("\\b" ++ Rest, S, Acc) ->
350 tokenize_string(Rest, ?ADV_COL(S, 2), [$\b | Acc]);
351 tokenize_string("\\f" ++ Rest, S, Acc) ->
352 tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]);
353 tokenize_string("\\n" ++ Rest, S, Acc) ->
354 tokenize_string(Rest, ?ADV_COL(S, 2), [$\n | Acc]);
355 tokenize_string("\\r" ++ Rest, S, Acc) ->
356 tokenize_string(Rest, ?ADV_COL(S, 2), [$\r | Acc]);
357 tokenize_string("\\t" ++ Rest, S, Acc) ->
358 tokenize_string(Rest, ?ADV_COL(S, 2), [$\t | Acc]);
359 tokenize_string([$\\, $u, C3, C2, C1, C0 | Rest], S, Acc) ->
360 % coalesce UTF-16 surrogate pair?
361 C = dehex(C0) bor
362 (dehex(C1) bsl 4) bor
363 (dehex(C2) bsl 8) bor
364 (dehex(C3) bsl 12),
365 tokenize_string(Rest, ?ADV_COL(S, 6), [C | Acc]);
366 tokenize_string([C | Rest], S, Acc) when C >= $\s; C < 16#10FFFF ->
367 tokenize_string(Rest, ?ADV_COL(S, 1), [C | Acc]).
368
369 tokenize_number(IoList=[C | _], Mode, S=#decoder{input_encoding=utf8}, Acc)
370 when is_list(C); is_binary(C); C >= 16#7f ->
371 List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)),
372 tokenize_number(List, Mode, S#decoder{input_encoding=unicode}, Acc);
373 tokenize_number([$- | Rest], sign, S, []) ->
374 tokenize_number(Rest, int, ?INC_COL(S), [$-]);
375 tokenize_number(Rest, sign, S, []) ->
376 tokenize_number(Rest, int, S, []);
377 tokenize_number([$0 | Rest], int, S, Acc) ->
378 tokenize_number(Rest, frac, ?INC_COL(S), [$0 | Acc]);
379 tokenize_number([C | Rest], int, S, Acc) when C >= $1, C =< $9 ->
380 tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
381 tokenize_number([C | Rest], int1, S, Acc) when C >= $0, C =< $9 ->
382 tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]);
383 tokenize_number(Rest, int1, S, Acc) ->
384 tokenize_number(Rest, frac, S, Acc);
385 tokenize_number([$., C | Rest], frac, S, Acc) when C >= $0, C =< $9 ->
386 tokenize_number(Rest, frac1, ?ADV_COL(S, 2), [C, $. | Acc]);
387 tokenize_number([E | Rest], frac, S, Acc) when E == $e; E == $E ->
388 tokenize_number(Rest, esign, ?INC_COL(S), [$e, $0, $. | Acc]);
389 tokenize_number(Rest, frac, S, Acc) ->
390 {{int, lists:reverse(Acc)}, Rest, S};
391 tokenize_number([C | Rest], frac1, S, Acc) when C >= $0, C =< $9 ->
392 tokenize_number(Rest, frac1, ?INC_COL(S), [C | Acc]);
393 tokenize_number([E | Rest], frac1, S, Acc) when E == $e; E == $E ->
394 tokenize_number(Rest, esign, ?INC_COL(S), [$e | Acc]);
395 tokenize_number(Rest, frac1, S, Acc) ->
396 {{float, lists:reverse(Acc)}, Rest, S};
397 tokenize_number([C | Rest], esign, S, Acc) when C == $-; C == $+ ->
398 tokenize_number(Rest, eint, ?INC_COL(S), [C | Acc]);
399 tokenize_number(Rest, esign, S, Acc) ->
400 tokenize_number(Rest, eint, S, Acc);
401 tokenize_number([C | Rest], eint, S, Acc) when C >= $0, C =< $9 ->
402 tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
403 tokenize_number([C | Rest], eint1, S, Acc) when C >= $0, C =< $9 ->
404 tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]);
405 tokenize_number(Rest, eint1, S, Acc) ->
406 {{float, lists:reverse(Acc)}, Rest, S}.
407
408 tokenize([], S=#decoder{state=trim}) ->
409 {eof, [], S};
410 tokenize([L | Rest], S) when is_list(L) ->
411 tokenize(L ++ Rest, S);
412 tokenize([B | Rest], S) when is_binary(B) ->
413 tokenize(xmerl_ucs:from_utf8(B) ++ Rest, S);
414 tokenize("\r\n" ++ Rest, S) ->
415 tokenize(Rest, ?INC_LINE(S));
416 tokenize("\n" ++ Rest, S) ->
417 tokenize(Rest, ?INC_LINE(S));
418 tokenize([C | Rest], S) when C == $\s; C == $\t ->
419 tokenize(Rest, ?INC_COL(S));
420 tokenize("{" ++ Rest, S) ->
421 {start_object, Rest, ?INC_COL(S)};
422 tokenize("}" ++ Rest, S) ->
423 {end_object, Rest, ?INC_COL(S)};
424 tokenize("[" ++ Rest, S) ->
425 {start_array, Rest, ?INC_COL(S)};
426 tokenize("]" ++ Rest, S) ->
427 {end_array, Rest, ?INC_COL(S)};
428 tokenize("," ++ Rest, S) ->
429 {comma, Rest, ?INC_COL(S)};
430 tokenize(":" ++ Rest, S) ->
431 {colon, Rest, ?INC_COL(S)};
432 tokenize("null" ++ Rest, S) ->
433 {{const, null}, Rest, ?ADV_COL(S, 4)};
434 tokenize("true" ++ Rest, S) ->
435 {{const, true}, Rest, ?ADV_COL(S, 4)};
436 tokenize("false" ++ Rest, S) ->
437 {{const, false}, Rest, ?ADV_COL(S, 5)};
438 tokenize("\"" ++ Rest, S) ->
439 {String, Rest1, S1} = tokenize_string(Rest, ?INC_COL(S), []),
440 {{const, String}, Rest1, S1};
441 tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- ->
442 case tokenize_number(L, sign, S, []) of
443 {{int, Int}, Rest, S1} ->
444 {{const, list_to_integer(Int)}, Rest, S1};
445 {{float, Float}, Rest, S1} ->
446 {{const, list_to_float(Float)}, Rest, S1}
447 end.
448
449 %% testing constructs borrowed from the Yaws JSON implementation.
450
451 %% Create an object from a list of Key/Value pairs.
452
453 obj_new() ->
454 {struct, []}.
455
456 is_obj({struct, Props}) ->
457 F = fun ({K, _}) when is_list(K) ->
458 true;
459 (_) ->
460 false
461 end,
462 lists:all(F, Props).
463
464 obj_from_list(Props) ->
465 Obj = {struct, Props},
466 case is_obj(Obj) of
467 true -> Obj;
468 false -> exit(json_bad_object)
469 end.
470
471 %% Test for equivalence of Erlang terms.
472 %% Due to arbitrary order of construction, equivalent objects might
473 %% compare unequal as erlang terms, so we need to carefully recurse
474 %% through aggregates (tuples and objects).
475
476 equiv({struct, Props1}, {struct, Props2}) ->
477 equiv_object(Props1, Props2);
478 equiv({array, L1}, {array, L2}) ->
479 equiv_list(L1, L2);
480 equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2;
481 equiv(S1, S2) when is_list(S1), is_list(S2) -> S1 == S2;
482 equiv(true, true) -> true;
483 equiv(false, false) -> true;
484 equiv(null, null) -> true.
485
486 %% Object representation and traversal order is unknown.
487 %% Use the sledgehammer and sort property lists.
488
489 equiv_object(Props1, Props2) ->
490 L1 = lists:keysort(1, Props1),
491 L2 = lists:keysort(1, Props2),
492 Pairs = lists:zip(L1, L2),
493 true = lists:all(fun({{K1, V1}, {K2, V2}}) ->
494 equiv(K1, K2) and equiv(V1, V2)
495 end, Pairs).
496
497 %% Recursively compare tuple elements for equivalence.
498
499 equiv_list([], []) ->
500 true;
501 equiv_list([V1 | L1], [V2 | L2]) ->
502 case equiv(V1, V2) of
503 true ->
504 equiv_list(L1, L2);
505 false ->
506 false
507 end.
508
509 test_all() ->
510 test_one(e2j_test_vec(utf8), 1).
511
512 test_one([], _N) ->
513 %% io:format("~p tests passed~n", [N-1]),
514 ok;
515 test_one([{E, J} | Rest], N) ->
516 %% io:format("[~p] ~p ~p~n", [N, E, J]),
517 true = equiv(E, decode(J)),
518 true = equiv(E, decode(encode(E))),
519 test_one(Rest, 1+N).
520
521 e2j_test_vec(unicode) ->
522 [
523 {"foo" ++ [500] ++ "bar", [$", $f, $o, $o, 500, $b, $a, $r, $"]}
524 ];
525 e2j_test_vec(utf8) ->
526 [
527 {1, "1"},
528 {3.1416, "3.14160"}, % text representation may truncate, trail zeroes
529 {-1, "-1"},
530 {-3.1416, "-3.14160"},
531 {12.0e10, "1.20000e+11"},
532 {1.234E+10, "1.23400e+10"},
533 {-1.234E-10, "-1.23400e-10"},
534 {10.0, "1.0e+01"},
535 {123.456, "1.23456E+2"},
536 {10.0, "1e1"},
537 {"foo", "\"foo\""},
538 {"foo" ++ [5] ++ "bar", "\"foo\\u0005bar\""},
539 {"", "\"\""},
540 {[], "\"\""},
541 {"\n\n\n", "\"\\n\\n\\n\""},
542 {obj_new(), "{}"},
543 {obj_from_list([{"foo", "bar"}]), "{\"foo\":\"bar\"}"},
544 {obj_from_list([{"foo", "bar"}, {"baz", 123}]),
545 "{\"foo\":\"bar\",\"baz\":123}"},
546 {{array, []}, "[]"},
547 {{array, [{array, []}]}, "[[]]"},
548 {{array, [1, "foo"]}, "[1,\"foo\"]"},
549
550 % json array in a json object
551 {obj_from_list([{"foo", {array, [123]}}]),
552 "{\"foo\":[123]}"},
553
554 % json object in a json object
555 {obj_from_list([{"foo", obj_from_list([{"bar", true}])}]),
556 "{\"foo\":{\"bar\":true}}"},
557
558 % fold evaluation order
559 {obj_from_list([{"foo", {array, []}},
560 {"bar", obj_from_list([{"baz", true}])},
561 {"alice", "bob"}]),
562 "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"},
563
564 % json object in a json array
565 {{array, [-123, "foo", obj_from_list([{"bar", {array, []}}]), null]},
566 "[-123,\"foo\",{\"bar\":[]},null]"}
567 ].
Something went wrong with that request. Please try again.