capricorn 2.0.8 → 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/erlang/lib/capricorn/ebin/capricorn.app +2 -1
- data/erlang/lib/capricorn/include/capricorn.hrl +1 -0
- data/erlang/lib/capricorn/src/cap_cluster_gems.erl +6 -1
- data/erlang/lib/capricorn/src/cap_console_dispatcher.erl +214 -0
- data/erlang/lib/capricorn/src/cap_machine_apps.erl +25 -1
- data/erlang/lib/capricorn/src/cap_sup.erl +14 -0
- data/erlang/lib/ejson/Makefile +24 -0
- data/erlang/lib/ejson/ebin/ejson.app +9 -0
- data/erlang/lib/ejson/include/ejson.hrl +40 -0
- data/erlang/lib/ejson/rebar.config +3 -0
- data/erlang/lib/ejson/src/ejson.erl +22 -0
- data/erlang/lib/ejson/src/ejson_decode.erl +337 -0
- data/erlang/lib/ejson/src/ejson_encode.erl +124 -0
- data/erlang/lib/ejson/test/arrays.escript +47 -0
- data/erlang/lib/ejson/test/compound.escript +56 -0
- data/erlang/lib/ejson/test/literals.escript +30 -0
- data/erlang/lib/ejson/test/numbers.escript +70 -0
- data/erlang/lib/ejson/test/objects.escript +51 -0
- data/erlang/lib/ejson/test/strings.escript +49 -0
- data/erlang/lib/ejson/test/timing.escript +43 -0
- data/erlang/lib/ejson/test/timing.json +382 -0
- data/erlang/lib/ejson/vendor/mochijson2.erl +621 -0
- data/erlang/lib/ejson/vendor/rfc4627.erl +625 -0
- data/erlang/lib/misultin/LICENSE.txt +41 -0
- data/erlang/lib/misultin/Makefile +26 -0
- data/erlang/lib/misultin/README.txt +120 -0
- data/erlang/lib/misultin/ebin/misultin.app +9 -0
- data/erlang/lib/misultin/examples/misultin_compress.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_echo.erl +58 -0
- data/erlang/lib/misultin/examples/misultin_file.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_gen_server.erl +158 -0
- data/erlang/lib/misultin/examples/misultin_get_variable.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_hello_world.erl +43 -0
- data/erlang/lib/misultin/examples/misultin_rest.erl +68 -0
- data/erlang/lib/misultin/examples/misultin_ssl.erl +51 -0
- data/erlang/lib/misultin/examples/misultin_stream.erl +55 -0
- data/erlang/lib/misultin/examples/misultin_websocket_event_example.erl +103 -0
- data/erlang/lib/misultin/examples/misultin_websocket_example.erl +95 -0
- data/erlang/lib/misultin/include/misultin.hrl +95 -0
- data/erlang/lib/misultin/make.bat +55 -0
- data/erlang/lib/misultin/priv/README.txt +12 -0
- data/erlang/lib/misultin/priv/test_certificate.pem +21 -0
- data/erlang/lib/misultin/priv/test_privkey.pem +18 -0
- data/erlang/lib/misultin/rebar.config +3 -0
- data/erlang/lib/misultin/src/misultin.app.src +9 -0
- data/erlang/lib/misultin/src/misultin.erl +338 -0
- data/erlang/lib/misultin/src/misultin_http.erl +488 -0
- data/erlang/lib/misultin/src/misultin_req.erl +280 -0
- data/erlang/lib/misultin/src/misultin_socket.erl +193 -0
- data/erlang/lib/misultin/src/misultin_utility.erl +357 -0
- data/erlang/lib/misultin/src/misultin_websocket.erl +252 -0
- data/erlang/lib/misultin/src/misultin_ws.erl +78 -0
- data/erlang/rebar.config +2 -0
- data/erlang/rel/overlay/etc/capricorn/app.config +4 -0
- data/erlang/rel/reltool.config +5 -0
- data/lib/capricorn/recipes/apache-debian.rb +1 -1
- data/lib/capricorn/recipes/centos-plesk.rb +1 -1
- data/lib/capricorn/recipes/debian-plesk95.rb +1 -2
- data/lib/capricorn/recipes/macports.rb +1 -1
- data/lib/capricorn/version.rb +1 -1
- metadata +51 -4
@@ -0,0 +1,625 @@
|
|
1
|
+
%% JSON - RFC 4627 - for Erlang
|
2
|
+
%%---------------------------------------------------------------------------
|
3
|
+
%% @author Tony Garnock-Jones <tonyg@kcbbs.gen.nz>
|
4
|
+
%% @author LShift Ltd. <query@lshift.net>
|
5
|
+
%% @copyright 2007, 2008 Tony Garnock-Jones and LShift Ltd.
|
6
|
+
%% @license
|
7
|
+
%%
|
8
|
+
%% Permission is hereby granted, free of charge, to any person
|
9
|
+
%% obtaining a copy of this software and associated documentation
|
10
|
+
%% files (the "Software"), to deal in the Software without
|
11
|
+
%% restriction, including without limitation the rights to use, copy,
|
12
|
+
%% modify, merge, publish, distribute, sublicense, and/or sell copies
|
13
|
+
%% of the Software, and to permit persons to whom the Software is
|
14
|
+
%% furnished to do so, subject to the following conditions:
|
15
|
+
%%
|
16
|
+
%% The above copyright notice and this permission notice shall be
|
17
|
+
%% included in all copies or substantial portions of the Software.
|
18
|
+
%%
|
19
|
+
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
22
|
+
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
23
|
+
%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
24
|
+
%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
%% SOFTWARE.
|
27
|
+
%%---------------------------------------------------------------------------
|
28
|
+
%%
|
29
|
+
%% @reference <a href="http://www.ietf.org/rfc/rfc4627.txt">RFC
|
30
|
+
%% 4627</a>, the JSON RFC
|
31
|
+
%%
|
32
|
+
%% @reference <a href="http://www.json.org/">JSON in general</a>
|
33
|
+
%%
|
34
|
+
%% @reference Joe Armstrong's <a
|
35
|
+
%% href="http://www.erlang.org/ml-archive/erlang-questions/200511/msg00193.html">
|
36
|
+
%% message</a> describing the basis of the JSON data type mapping that
|
37
|
+
%% this module uses
|
38
|
+
%%
|
39
|
+
%% @doc An implementation of RFC 4627 (JSON, the JavaScript Object Notation) for Erlang.
|
40
|
+
%%
|
41
|
+
%% The basic API is comprised of the {@link encode/1} and {@link decode/1} functions.
|
42
|
+
%%
|
43
|
+
%% == Data Type Mapping ==
|
44
|
+
%%
|
45
|
+
%% The data type mapping I've implemented is as per Joe Armstrong's
|
46
|
+
%% message [http://www.erlang.org/ml-archive/erlang-questions/200511/msg00193.html] - see {@link json()}.
|
47
|
+
%%
|
48
|
+
%% == Unicode ==
|
49
|
+
%%
|
50
|
+
%% When serializing a string, if characters are found with codepoint
|
51
|
+
%% >127, we rely on the unicode encoder to build the proper byte
|
52
|
+
%% sequence for transmission. We still use the \uXXXX escape for
|
53
|
+
%% control characters (other than the RFC-specified specially
|
54
|
+
%% recognised ones).
|
55
|
+
%%
|
56
|
+
%% {@link decode/1} will autodetect the unicode encoding used, and any
|
57
|
+
%% strings returned in the result as binaries will contain UTF-8
|
58
|
+
%% encoded byte sequences for codepoints >127. Object keys containing
|
59
|
+
%% codepoints >127 will be returned as lists of codepoints, rather
|
60
|
+
%% than being UTF-8 encoded. If you have already transformed the text
|
61
|
+
%% to parse into a list of unicode codepoints, perhaps by your own use
|
62
|
+
%% of {@link unicode_decode/1}, then use {@link decode_noauto/1} to
|
63
|
+
%% avoid redundant and erroneous double-unicode-decoding.
|
64
|
+
%%
|
65
|
+
%% Similarly, {@link encode/1} produces text that is already UTF-8
|
66
|
+
%% encoded. To get raw codepoints, use {@link encode_noauto/1} and
|
67
|
+
%% {@link encode_noauto/2}. You can use {@link unicode_encode/1} to
|
68
|
+
%% UTF-encode the results, if that's appropriate for your application.
|
69
|
+
%%
|
70
|
+
%% == Differences to the specification ==
|
71
|
+
%%
|
72
|
+
%% I'm lenient in the following ways during parsing:
|
73
|
+
%%
|
74
|
+
%% <ul>
|
75
|
+
%% <li>repeated commas in arrays and objects collapse to a single comma</li>
|
76
|
+
%% <li>any character =<32 is considered whitespace</li>
|
77
|
+
%% <li>leading zeros for numbers are accepted</li>
|
78
|
+
%% <li>we don't restrict the toplevel token to only object or array -
|
79
|
+
%% any JSON value can be used at toplevel</li>
|
80
|
+
%% </ul>
|
81
|
+
|
82
|
+
%% @type json() = jsonobj() | jsonarray() | jsonnum() | jsonstr() | true | false | null. An Erlang representation of a general JSON value.
|
83
|
+
%% @type jsonobj() = {obj, [{jsonkey(), json()}]}. A JSON "object" or "struct".
|
84
|
+
%% @type jsonkey() = string(). A field-name within a JSON "object".
|
85
|
+
%% @type jsonarray() = [json()]. A JSON array value.
|
86
|
+
%% @type jsonnum() = integer() | float(). A JSON numeric value.
|
87
|
+
%% @type jsonstr() = binary(). A JSON string value.
|
88
|
+
%% @type byte() = integer(). An integer >=0 and =<255.
|
89
|
+
|
90
|
+
-module(rfc4627).
|
91
|
+
|
92
|
+
-export([mime_type/0, encode/1, decode/1]).
|
93
|
+
-export([encode_noauto/1, encode_noauto/2, decode_noauto/1]).
|
94
|
+
-export([unicode_decode/1, unicode_encode/1]).
|
95
|
+
-export([from_record/3, to_record/3]).
|
96
|
+
-export([hex_digit/1, digit_hex/1]).
|
97
|
+
-export([get_field/2, get_field/3, set_field/3]).
|
98
|
+
-export([equiv/2]).
|
99
|
+
|
100
|
+
%% @spec () -> string()
|
101
|
+
%% @doc Returns the IANA-registered MIME type for JSON data.
|
102
|
+
mime_type() ->
|
103
|
+
"application/json".
|
104
|
+
|
105
|
+
%% @spec (json()) -> [byte()]
|
106
|
+
%%
|
107
|
+
%% @doc Encodes the JSON value supplied, first into Unicode
|
108
|
+
%% codepoints, and then into UTF-8.
|
109
|
+
%%
|
110
|
+
%% The resulting string is a list of byte values that should be
|
111
|
+
%% interpreted as UTF-8 encoded text.
|
112
|
+
%%
|
113
|
+
%% During encoding, atoms and binaries are accepted as keys of JSON
|
114
|
+
%% objects (type {@link jsonkey()}) as well as the usual strings
|
115
|
+
%% (lists of character codepoints).
|
116
|
+
encode(X) ->
|
117
|
+
unicode_encode({'utf-8', encode_noauto(X)}).
|
118
|
+
|
119
|
+
%% @spec (json()) -> string()
|
120
|
+
%%
|
121
|
+
%% @doc Encodes the JSON value supplied into raw Unicode codepoints.
|
122
|
+
%%
|
123
|
+
%% The resulting string may contain codepoints with value >=128. You
|
124
|
+
%% can use {@link unicode_encode/1} to UTF-encode the results, if
|
125
|
+
%% that's appropriate for your application.
|
126
|
+
%%
|
127
|
+
%% During encoding, atoms and binaries are accepted as keys of JSON
|
128
|
+
%% objects (type {@link jsonkey()}) as well as the usual strings
|
129
|
+
%% (lists of character codepoints).
|
130
|
+
encode_noauto(X) ->
|
131
|
+
lists:reverse(encode_noauto(X, [])).
|
132
|
+
|
133
|
+
%% @spec (json(), string()) -> string()
|
134
|
+
%%
|
135
|
+
%% @doc As {@link encode_noauto/1}, but prepends <i>reversed</i> text
|
136
|
+
%% to the supplied accumulator string.
|
137
|
+
encode_noauto(true, Acc) ->
|
138
|
+
"eurt" ++ Acc;
|
139
|
+
encode_noauto(false, Acc) ->
|
140
|
+
"eslaf" ++ Acc;
|
141
|
+
encode_noauto(null, Acc) ->
|
142
|
+
"llun" ++ Acc;
|
143
|
+
encode_noauto(Str, Acc) when is_binary(Str) ->
|
144
|
+
Codepoints = xmerl_ucs:from_utf8(Str),
|
145
|
+
quote_and_encode_string(Codepoints, Acc);
|
146
|
+
encode_noauto(Str, Acc) when is_atom(Str) ->
|
147
|
+
quote_and_encode_string(atom_to_list(Str), Acc);
|
148
|
+
encode_noauto(Num, Acc) when is_number(Num) ->
|
149
|
+
encode_number(Num, Acc);
|
150
|
+
encode_noauto({obj, Fields}, Acc) ->
|
151
|
+
"}" ++ encode_object(Fields, "{" ++ Acc);
|
152
|
+
encode_noauto(Dict, Acc) when element(1, Dict) =:= dict ->
|
153
|
+
"}" ++ encode_object(dict:to_list(Dict), "{" ++ Acc);
|
154
|
+
encode_noauto(Arr, Acc) when is_list(Arr) ->
|
155
|
+
"]" ++ encode_array(Arr, "[" ++ Acc).
|
156
|
+
|
157
|
+
encode_object([], Acc) ->
|
158
|
+
Acc;
|
159
|
+
encode_object([{Key, Value}], Acc) ->
|
160
|
+
encode_field(Key, Value, Acc);
|
161
|
+
encode_object([{Key, Value} | Rest], Acc) ->
|
162
|
+
encode_object(Rest, "," ++ encode_field(Key, Value, Acc)).
|
163
|
+
|
164
|
+
encode_field(Key, Value, Acc) when is_binary(Key) ->
|
165
|
+
Codepoints = xmerl_ucs:from_utf8(Key),
|
166
|
+
encode_noauto(Value, ":" ++ quote_and_encode_string(Codepoints, Acc));
|
167
|
+
encode_field(Key, Value, Acc) when is_atom(Key) ->
|
168
|
+
encode_noauto(Value, ":" ++ quote_and_encode_string(atom_to_list(Key), Acc));
|
169
|
+
encode_field(Key, Value, Acc) when is_list(Key) ->
|
170
|
+
encode_noauto(Value, ":" ++ quote_and_encode_string(Key, Acc)).
|
171
|
+
|
172
|
+
encode_array([], Acc) ->
|
173
|
+
Acc;
|
174
|
+
encode_array([X], Acc) ->
|
175
|
+
encode_noauto(X, Acc);
|
176
|
+
encode_array([X | Rest], Acc) ->
|
177
|
+
encode_array(Rest, "," ++ encode_noauto(X, Acc)).
|
178
|
+
|
179
|
+
quote_and_encode_string(Str, Acc) ->
|
180
|
+
"\"" ++ encode_string(Str, "\"" ++ Acc).
|
181
|
+
|
182
|
+
encode_string([], Acc) ->
|
183
|
+
Acc;
|
184
|
+
encode_string([$" | Rest], Acc) ->
|
185
|
+
encode_string(Rest, [$", $\\ | Acc]);
|
186
|
+
encode_string([$\\ | Rest], Acc) ->
|
187
|
+
encode_string(Rest, [$\\, $\\ | Acc]);
|
188
|
+
encode_string([X | Rest], Acc) when X < 32 orelse X > 127 ->
|
189
|
+
encode_string(Rest, encode_general_char(X, Acc));
|
190
|
+
encode_string([X | Rest], Acc) ->
|
191
|
+
encode_string(Rest, [X | Acc]).
|
192
|
+
|
193
|
+
encode_general_char(8, Acc) -> [$b, $\\ | Acc];
|
194
|
+
encode_general_char(9, Acc) -> [$t, $\\ | Acc];
|
195
|
+
encode_general_char(10, Acc) -> [$n, $\\ | Acc];
|
196
|
+
encode_general_char(12, Acc) -> [$f, $\\ | Acc];
|
197
|
+
encode_general_char(13, Acc) -> [$r, $\\ | Acc];
|
198
|
+
encode_general_char(X, Acc) when X > 127 -> [X | Acc];
|
199
|
+
encode_general_char(X, Acc) ->
|
200
|
+
%% FIXME currently this branch never runs.
|
201
|
+
%% We could make it configurable, maybe?
|
202
|
+
Utf16Bytes = xmerl_ucs:to_utf16be(X),
|
203
|
+
encode_utf16be_chars(Utf16Bytes, Acc).
|
204
|
+
|
205
|
+
encode_utf16be_chars([], Acc) ->
|
206
|
+
Acc;
|
207
|
+
encode_utf16be_chars([B1, B2 | Rest], Acc) ->
|
208
|
+
encode_utf16be_chars(Rest, [hex_digit((B2) band 16#F),
|
209
|
+
hex_digit((B2 bsr 4) band 16#F),
|
210
|
+
hex_digit((B1) band 16#F),
|
211
|
+
hex_digit((B1 bsr 4) band 16#F),
|
212
|
+
$u,
|
213
|
+
$\\ | Acc]).
|
214
|
+
|
215
|
+
%% @spec (Nibble::integer()) -> char()
|
216
|
+
%% @doc Returns the character code corresponding to Nibble.
|
217
|
+
%%
|
218
|
+
%% Nibble must be >=0 and =<16.
|
219
|
+
hex_digit(0) -> $0;
|
220
|
+
hex_digit(1) -> $1;
|
221
|
+
hex_digit(2) -> $2;
|
222
|
+
hex_digit(3) -> $3;
|
223
|
+
hex_digit(4) -> $4;
|
224
|
+
hex_digit(5) -> $5;
|
225
|
+
hex_digit(6) -> $6;
|
226
|
+
hex_digit(7) -> $7;
|
227
|
+
hex_digit(8) -> $8;
|
228
|
+
hex_digit(9) -> $9;
|
229
|
+
hex_digit(10) -> $A;
|
230
|
+
hex_digit(11) -> $B;
|
231
|
+
hex_digit(12) -> $C;
|
232
|
+
hex_digit(13) -> $D;
|
233
|
+
hex_digit(14) -> $E;
|
234
|
+
hex_digit(15) -> $F.
|
235
|
+
|
236
|
+
encode_number(Num, Acc) when is_integer(Num) ->
|
237
|
+
lists:reverse(integer_to_list(Num), Acc);
|
238
|
+
encode_number(Num, Acc) when is_float(Num) ->
|
239
|
+
lists:reverse(float_to_list(Num), Acc).
|
240
|
+
|
241
|
+
%% @spec (Input::(binary() | [byte()])) -> ({ok, json(), Remainder} | {error, Reason})
|
242
|
+
%% where Remainder = string()
|
243
|
+
%% Reason = any()
|
244
|
+
%%
|
245
|
+
%% @doc Decodes a JSON value from an input binary or string of
|
246
|
+
%% Unicode-encoded text.
|
247
|
+
%%
|
248
|
+
%% Given a binary, converts it to a list of bytes. Given a
|
249
|
+
%% list/string, interprets it as a list of bytes.
|
250
|
+
%%
|
251
|
+
%% Uses {@link unicode_decode/1} on its input, which results in a list
|
252
|
+
%% of codepoints, and then decodes a JSON value from that list of
|
253
|
+
%% codepoints.
|
254
|
+
%%
|
255
|
+
%% Returns either `{ok, Result, Remainder}', where Remainder is the
|
256
|
+
%% remaining portion of the input that was not consumed in the process
|
257
|
+
%% of decoding Result, or `{error, Reason}'.
|
258
|
+
decode(Bin) when is_binary(Bin) ->
|
259
|
+
decode(binary_to_list(Bin));
|
260
|
+
decode(Bytes) ->
|
261
|
+
{_Charset, Codepoints} = unicode_decode(Bytes),
|
262
|
+
decode_noauto(Codepoints).
|
263
|
+
|
264
|
+
%% @spec (Input::string()) -> ({ok, json(), string()} | {error, any()})
|
265
|
+
%%
|
266
|
+
%% @doc As {@link decode/1}, but does not perform Unicode decoding on its input.
|
267
|
+
%%
|
268
|
+
%% Expects a list of codepoints - an ordinary Erlang string - rather
|
269
|
+
%% than a list of Unicode-encoded bytes.
|
270
|
+
decode_noauto(Bin) when is_binary(Bin) ->
|
271
|
+
decode_noauto(binary_to_list(Bin));
|
272
|
+
decode_noauto(Chars) ->
|
273
|
+
case catch parse(skipws(Chars)) of
|
274
|
+
{'EXIT', Reason} ->
|
275
|
+
%% Reason is usually far too much information, but helps
|
276
|
+
%% if needing to debug this module.
|
277
|
+
{error, Reason};
|
278
|
+
{Value, Remaining} ->
|
279
|
+
{ok, Value, skipws(Remaining)}
|
280
|
+
end.
|
281
|
+
|
282
|
+
%% @spec ([byte()]) -> [char()]
|
283
|
+
%%
|
284
|
+
%% @doc Autodetects and decodes using the Unicode encoding of its input.
|
285
|
+
%%
|
286
|
+
%% From RFC4627, section 3, "Encoding":
|
287
|
+
%%
|
288
|
+
%% <blockquote>
|
289
|
+
%% JSON text SHALL be encoded in Unicode. The default encoding is
|
290
|
+
%% UTF-8.
|
291
|
+
%%
|
292
|
+
%% Since the first two characters of a JSON text will always be ASCII
|
293
|
+
%% characters [RFC0020], it is possible to determine whether an octet
|
294
|
+
%% stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
|
295
|
+
%% at the pattern of nulls in the first four octets.
|
296
|
+
%%
|
297
|
+
%% 00 00 00 xx UTF-32BE
|
298
|
+
%% 00 xx 00 xx UTF-16BE
|
299
|
+
%% xx 00 00 00 UTF-32LE
|
300
|
+
%% xx 00 xx 00 UTF-16LE
|
301
|
+
%% xx xx xx xx UTF-8
|
302
|
+
%% </blockquote>
|
303
|
+
%%
|
304
|
+
%% Interestingly, the BOM (byte-order mark) is not mentioned. We
|
305
|
+
%% support it here by using it to detect our encoding, discarding it
|
306
|
+
%% if present, even though RFC4627 explicitly notes that the first two
|
307
|
+
%% characters of a JSON text will be ASCII.
|
308
|
+
%%
|
309
|
+
%% If a BOM ([http://unicode.org/faq/utf_bom.html]) is present, we use
|
310
|
+
%% that; if not, we use RFC4627's rules (as above). Note that UTF-32
|
311
|
+
%% is the same as UCS-4 for our purposes (but see also
|
312
|
+
%% [http://unicode.org/reports/tr19/tr19-9.html]). Note that UTF-16 is
|
313
|
+
%% not the same as UCS-2!
|
314
|
+
%%
|
315
|
+
%% Note that I'm using xmerl's UCS/UTF support here. There's another
|
316
|
+
%% UTF-8 codec in asn1rt, which works on binaries instead of lists.
|
317
|
+
%%
|
318
|
+
unicode_decode([0,0,254,255|C]) -> {'utf-32', xmerl_ucs:from_ucs4be(C)};
|
319
|
+
unicode_decode([255,254,0,0|C]) -> {'utf-32', xmerl_ucs:from_ucs4le(C)};
|
320
|
+
unicode_decode([254,255|C]) -> {'utf-16', xmerl_ucs:from_utf16be(C)};
|
321
|
+
unicode_decode([239,187,191|C]) -> {'utf-8', xmerl_ucs:from_utf8(C)};
|
322
|
+
unicode_decode(C=[0,0,_,_|_]) -> {'utf-32be', xmerl_ucs:from_ucs4be(C)};
|
323
|
+
unicode_decode(C=[_,_,0,0|_]) -> {'utf-32le', xmerl_ucs:from_ucs4le(C)};
|
324
|
+
unicode_decode(C=[0,_|_]) -> {'utf-16be', xmerl_ucs:from_utf16be(C)};
|
325
|
+
unicode_decode(C=[_,0|_]) -> {'utf-16le', xmerl_ucs:from_utf16le(C)};
|
326
|
+
unicode_decode(C=_) -> {'utf-8', xmerl_ucs:from_utf8(C)}.
|
327
|
+
|
328
|
+
%% @spec (EncodingAndCharacters::{Encoding, [char()]}) -> [byte()]
|
329
|
+
%% where Encoding = 'utf-32' | 'utf-32be' | 'utf-32le' | 'utf-16' |
|
330
|
+
%% 'utf-16be' | 'utf-16le' | 'utf-8'
|
331
|
+
%%
|
332
|
+
%% @doc Encodes the given characters to bytes, using the given Unicode encoding.
|
333
|
+
%%
|
334
|
+
%% For convenience, we supply a partial inverse of unicode_decode; If
|
335
|
+
%% a BOM is requested, we more-or-less arbitrarily pick the big-endian
|
336
|
+
%% variant of the encoding, since big-endian is network-order. We
|
337
|
+
%% don't support UTF-8 with BOM here.
|
338
|
+
unicode_encode({'utf-32', C}) -> [0,0,254,255|xmerl_ucs:to_ucs4be(C)];
|
339
|
+
unicode_encode({'utf-32be', C}) -> xmerl_ucs:to_ucs4be(C);
|
340
|
+
unicode_encode({'utf-32le', C}) -> xmerl_ucs:to_ucs4le(C);
|
341
|
+
unicode_encode({'utf-16', C}) -> [254,255|xmerl_ucs:to_utf16be(C)];
|
342
|
+
unicode_encode({'utf-16be', C}) -> xmerl_ucs:to_utf16be(C);
|
343
|
+
unicode_encode({'utf-16le', C}) -> xmerl_ucs:to_utf16le(C);
|
344
|
+
unicode_encode({'utf-8', C}) -> xmerl_ucs:to_utf8(C).
|
345
|
+
|
346
|
+
parse([$" | Rest]) -> %% " emacs balancing
|
347
|
+
{Codepoints, Rest1} = parse_string(Rest, []),
|
348
|
+
{list_to_binary(xmerl_ucs:to_utf8(Codepoints)), Rest1};
|
349
|
+
parse("true" ++ Rest) -> {true, Rest};
|
350
|
+
parse("false" ++ Rest) -> {false, Rest};
|
351
|
+
parse("null" ++ Rest) -> {null, Rest};
|
352
|
+
parse([${ | Rest]) -> parse_object(skipws(Rest), []);
|
353
|
+
parse([$[ | Rest]) -> parse_array(skipws(Rest), []);
|
354
|
+
parse([]) -> exit(unexpected_end_of_input);
|
355
|
+
parse(Chars) -> parse_number(Chars, []).
|
356
|
+
|
357
|
+
skipws([X | Rest]) when X =< 32 ->
|
358
|
+
skipws(Rest);
|
359
|
+
skipws(Chars) ->
|
360
|
+
Chars.
|
361
|
+
|
362
|
+
parse_string(Chars, Acc) ->
|
363
|
+
case parse_codepoint(Chars) of
|
364
|
+
{done, Rest} ->
|
365
|
+
{lists:reverse(Acc), Rest};
|
366
|
+
{ok, Codepoint, Rest} ->
|
367
|
+
parse_string(Rest, [Codepoint | Acc])
|
368
|
+
end.
|
369
|
+
|
370
|
+
parse_codepoint([$" | Rest]) -> %% " emacs balancing
|
371
|
+
{done, Rest};
|
372
|
+
parse_codepoint([$\\, Key | Rest]) ->
|
373
|
+
parse_general_char(Key, Rest);
|
374
|
+
parse_codepoint([X | Rest]) ->
|
375
|
+
{ok, X, Rest}.
|
376
|
+
|
377
|
+
parse_general_char($b, Rest) -> {ok, 8, Rest};
|
378
|
+
parse_general_char($t, Rest) -> {ok, 9, Rest};
|
379
|
+
parse_general_char($n, Rest) -> {ok, 10, Rest};
|
380
|
+
parse_general_char($f, Rest) -> {ok, 12, Rest};
|
381
|
+
parse_general_char($r, Rest) -> {ok, 13, Rest};
|
382
|
+
parse_general_char($/, Rest) -> {ok, $/, Rest};
|
383
|
+
parse_general_char($\\, Rest) -> {ok, $\\, Rest};
|
384
|
+
parse_general_char($", Rest) -> {ok, $", Rest};
|
385
|
+
parse_general_char($u, [D0, D1, D2, D3 | Rest]) ->
|
386
|
+
Codepoint =
|
387
|
+
(digit_hex(D0) bsl 12) +
|
388
|
+
(digit_hex(D1) bsl 8) +
|
389
|
+
(digit_hex(D2) bsl 4) +
|
390
|
+
(digit_hex(D3)),
|
391
|
+
if
|
392
|
+
Codepoint >= 16#D800 andalso Codepoint < 16#DC00 ->
|
393
|
+
% High half of surrogate pair
|
394
|
+
case parse_codepoint(Rest) of
|
395
|
+
{low_surrogate_pair, Codepoint2, Rest1} ->
|
396
|
+
[FinalCodepoint] =
|
397
|
+
xmerl_ucs:from_utf16be(<<Codepoint:16/big-unsigned-integer,
|
398
|
+
Codepoint2:16/big-unsigned-integer>>),
|
399
|
+
{ok, FinalCodepoint, Rest1};
|
400
|
+
_ ->
|
401
|
+
exit(incorrect_usage_of_surrogate_pair)
|
402
|
+
end;
|
403
|
+
Codepoint >= 16#DC00 andalso Codepoint < 16#E000 ->
|
404
|
+
{low_surrogate_pair, Codepoint, Rest};
|
405
|
+
true ->
|
406
|
+
{ok, Codepoint, Rest}
|
407
|
+
end.
|
408
|
+
|
409
|
+
%% @spec (Hexchar::char()) -> integer()
|
410
|
+
%% @doc Returns the number corresponding to Hexchar.
|
411
|
+
%%
|
412
|
+
%% Hexchar must be one of the characters `$0' through `$9', `$A'
|
413
|
+
%% through `$F' or `$a' through `$f'.
|
414
|
+
digit_hex($0) -> 0;
|
415
|
+
digit_hex($1) -> 1;
|
416
|
+
digit_hex($2) -> 2;
|
417
|
+
digit_hex($3) -> 3;
|
418
|
+
digit_hex($4) -> 4;
|
419
|
+
digit_hex($5) -> 5;
|
420
|
+
digit_hex($6) -> 6;
|
421
|
+
digit_hex($7) -> 7;
|
422
|
+
digit_hex($8) -> 8;
|
423
|
+
digit_hex($9) -> 9;
|
424
|
+
|
425
|
+
digit_hex($A) -> 10;
|
426
|
+
digit_hex($B) -> 11;
|
427
|
+
digit_hex($C) -> 12;
|
428
|
+
digit_hex($D) -> 13;
|
429
|
+
digit_hex($E) -> 14;
|
430
|
+
digit_hex($F) -> 15;
|
431
|
+
|
432
|
+
digit_hex($a) -> 10;
|
433
|
+
digit_hex($b) -> 11;
|
434
|
+
digit_hex($c) -> 12;
|
435
|
+
digit_hex($d) -> 13;
|
436
|
+
digit_hex($e) -> 14;
|
437
|
+
digit_hex($f) -> 15.
|
438
|
+
|
439
|
+
finish_number(Acc, Rest) ->
|
440
|
+
Str = lists:reverse(Acc),
|
441
|
+
{case catch list_to_integer(Str) of
|
442
|
+
{'EXIT', _} -> list_to_float(Str);
|
443
|
+
Value -> Value
|
444
|
+
end, Rest}.
|
445
|
+
|
446
|
+
parse_number([$- | Rest], Acc) ->
|
447
|
+
parse_number1(Rest, [$- | Acc]);
|
448
|
+
parse_number(Rest = [C | _], Acc) ->
|
449
|
+
case is_digit(C) of
|
450
|
+
true -> parse_number1(Rest, Acc);
|
451
|
+
false -> exit(syntax_error)
|
452
|
+
end.
|
453
|
+
|
454
|
+
parse_number1(Rest, Acc) ->
|
455
|
+
{Acc1, Rest1} = parse_int_part(Rest, Acc),
|
456
|
+
case Rest1 of
|
457
|
+
[] -> finish_number(Acc1, []);
|
458
|
+
[$. | More] ->
|
459
|
+
{Acc2, Rest2} = parse_int_part(More, [$. | Acc1]),
|
460
|
+
parse_exp(Rest2, Acc2, false);
|
461
|
+
_ ->
|
462
|
+
parse_exp(Rest1, Acc1, true)
|
463
|
+
end.
|
464
|
+
|
465
|
+
parse_int_part(Chars = [_Ch | _Rest], Acc) ->
|
466
|
+
parse_int_part0(Chars, Acc).
|
467
|
+
|
468
|
+
parse_int_part0([], Acc) ->
|
469
|
+
{Acc, []};
|
470
|
+
parse_int_part0([Ch | Rest], Acc) ->
|
471
|
+
case is_digit(Ch) of
|
472
|
+
true -> parse_int_part0(Rest, [Ch | Acc]);
|
473
|
+
false -> {Acc, [Ch | Rest]}
|
474
|
+
end.
|
475
|
+
|
476
|
+
parse_exp([$e | Rest], Acc, NeedFrac) ->
|
477
|
+
parse_exp1(Rest, Acc, NeedFrac);
|
478
|
+
parse_exp([$E | Rest], Acc, NeedFrac) ->
|
479
|
+
parse_exp1(Rest, Acc, NeedFrac);
|
480
|
+
parse_exp(Rest, Acc, _NeedFrac) ->
|
481
|
+
finish_number(Acc, Rest).
|
482
|
+
|
483
|
+
parse_exp1(Rest, Acc, NeedFrac) ->
|
484
|
+
{Acc1, Rest1} = parse_signed_int_part(Rest, if
|
485
|
+
NeedFrac -> [$e, $0, $. | Acc];
|
486
|
+
true -> [$e | Acc]
|
487
|
+
end),
|
488
|
+
finish_number(Acc1, Rest1).
|
489
|
+
|
490
|
+
parse_signed_int_part([$+ | Rest], Acc) ->
|
491
|
+
parse_int_part(Rest, [$+ | Acc]);
|
492
|
+
parse_signed_int_part([$- | Rest], Acc) ->
|
493
|
+
parse_int_part(Rest, [$- | Acc]);
|
494
|
+
parse_signed_int_part(Rest, Acc) ->
|
495
|
+
parse_int_part(Rest, Acc).
|
496
|
+
|
497
|
+
is_digit($0) -> true;
|
498
|
+
is_digit($1) -> true;
|
499
|
+
is_digit($2) -> true;
|
500
|
+
is_digit($3) -> true;
|
501
|
+
is_digit($4) -> true;
|
502
|
+
is_digit($5) -> true;
|
503
|
+
is_digit($6) -> true;
|
504
|
+
is_digit($7) -> true;
|
505
|
+
is_digit($8) -> true;
|
506
|
+
is_digit($9) -> true;
|
507
|
+
is_digit(_) -> false.
|
508
|
+
|
509
|
+
parse_object([$} | Rest], Acc) ->
|
510
|
+
{{obj, lists:reverse(Acc)}, Rest};
|
511
|
+
parse_object([$, | Rest], Acc) ->
|
512
|
+
parse_object(skipws(Rest), Acc);
|
513
|
+
parse_object([$" | Rest], Acc) -> %% " emacs balancing
|
514
|
+
{KeyCodepoints, Rest1} = parse_string(Rest, []),
|
515
|
+
[$: | Rest2] = skipws(Rest1),
|
516
|
+
{Value, Rest3} = parse(skipws(Rest2)),
|
517
|
+
parse_object(skipws(Rest3), [{KeyCodepoints, Value} | Acc]).
|
518
|
+
|
519
|
+
parse_array([$] | Rest], Acc) ->
|
520
|
+
{lists:reverse(Acc), Rest};
|
521
|
+
parse_array([$, | Rest], Acc) ->
|
522
|
+
parse_array(skipws(Rest), Acc);
|
523
|
+
parse_array(Chars, Acc) ->
|
524
|
+
{Value, Rest} = parse(Chars),
|
525
|
+
parse_array(skipws(Rest), [Value | Acc]).
|
526
|
+
|
527
|
+
%% @spec (Record, atom(), [any()]) -> jsonobj()
|
528
|
+
%% where Record = tuple()
|
529
|
+
%%
|
530
|
+
%% @doc Used by the `?RFC4627_FROM_RECORD' macro in `rfc4627.hrl'.
|
531
|
+
%%
|
532
|
+
%% Given a record type definiton of ``-record(myrecord, {field1,
|
533
|
+
%% field})'', and a value ``V = #myrecord{}'', the code
|
534
|
+
%% ``?RFC4627_FROM_RECORD(myrecord, V)'' will return a JSON "object"
|
535
|
+
%% with fields corresponding to the fields of the record. The macro
|
536
|
+
%% expands to a call to the `from_record' function.
|
537
|
+
from_record(R, _RecordName, Fields) ->
|
538
|
+
{obj, encode_record_fields(R, 2, Fields)}.
|
539
|
+
|
540
|
+
encode_record_fields(_R, _Index, []) ->
|
541
|
+
[];
|
542
|
+
encode_record_fields(R, Index, [Field | Rest]) ->
|
543
|
+
case element(Index, R) of
|
544
|
+
undefined ->
|
545
|
+
encode_record_fields(R, Index + 1, Rest);
|
546
|
+
Value ->
|
547
|
+
[{atom_to_list(Field), Value} | encode_record_fields(R, Index + 1, Rest)]
|
548
|
+
end.
|
549
|
+
|
550
|
+
%% @spec (JsonObject::jsonobj(), DefaultValue::Record, [atom()]) -> Record
|
551
|
+
%% where Record = tuple()
|
552
|
+
%%
|
553
|
+
%% @doc Used by the `?RFC4627_TO_RECORD' macro in `rfc4627.hrl'.
|
554
|
+
%%
|
555
|
+
%% Given a record type definiton of ``-record(myrecord, {field1,
|
556
|
+
%% field})'', and a JSON "object" ``J = {obj, [{"field1", 123},
|
557
|
+
%% {"field2", 234}]}'', the code ``?RFC4627_TO_RECORD(myrecord, J)''
|
558
|
+
%% will return a record ``#myrecord{field1 = 123, field2 = 234}''.
|
559
|
+
%% The macro expands to a call to the `to_record' function.
|
560
|
+
to_record({obj, Values}, Fallback, Fields) ->
|
561
|
+
list_to_tuple([element(1, Fallback) | decode_record_fields(Values, Fallback, 2, Fields)]).
|
562
|
+
|
563
|
+
decode_record_fields(_Values, _Fallback, _Index, []) ->
|
564
|
+
[];
|
565
|
+
decode_record_fields(Values, Fallback, Index, [Field | Rest]) ->
|
566
|
+
[case lists:keysearch(atom_to_list(Field), 1, Values) of
|
567
|
+
{value, {_, Value}} ->
|
568
|
+
Value;
|
569
|
+
false ->
|
570
|
+
element(Index, Fallback)
|
571
|
+
end | decode_record_fields(Values, Fallback, Index + 1, Rest)].
|
572
|
+
|
573
|
+
%% @spec (JsonObject::jsonobj(), atom()) -> {ok, json()} | not_found
|
574
|
+
%% @doc Retrieves the value of a named field of a JSON "object".
|
575
|
+
get_field({obj, Props}, Key) ->
|
576
|
+
case lists:keysearch(Key, 1, Props) of
|
577
|
+
{value, {_K, Val}} ->
|
578
|
+
{ok, Val};
|
579
|
+
false ->
|
580
|
+
not_found
|
581
|
+
end.
|
582
|
+
|
583
|
+
%% @spec (jsonobj(), atom(), json()) -> json()
|
584
|
+
%% @doc Retrieves the value of a named field of a JSON "object", or a
|
585
|
+
%% default value if no such field is present.
|
586
|
+
get_field(Obj, Key, DefaultValue) ->
|
587
|
+
case get_field(Obj, Key) of
|
588
|
+
{ok, Val} ->
|
589
|
+
Val;
|
590
|
+
not_found ->
|
591
|
+
DefaultValue
|
592
|
+
end.
|
593
|
+
|
594
|
+
%% @spec (JsonObject::jsonobj(), atom(), json()) -> jsonobj()
|
595
|
+
%% @doc Adds or replaces a named field with the given value.
|
596
|
+
%%
|
597
|
+
%% Returns a JSON "object" that contains the new field value as well
|
598
|
+
%% as all the unmodified fields from the first argument.
|
599
|
+
set_field({obj, Props}, Key, NewValue) ->
|
600
|
+
{obj, [{Key, NewValue} | lists:keydelete(Key, 1, Props)]}.
|
601
|
+
|
602
|
+
%% @spec (A::json(), B::json()) -> bool()
|
603
|
+
%% @doc Tests equivalence of JSON terms.
|
604
|
+
%%
|
605
|
+
%% After Bob Ippolito's `equiv' predicate in mochijson.
|
606
|
+
equiv({obj, Props1}, {obj, Props2}) ->
|
607
|
+
L1 = lists:keysort(1, Props1),
|
608
|
+
L2 = lists:keysort(1, Props2),
|
609
|
+
equiv_sorted_plists(L1, L2);
|
610
|
+
equiv(A, B) when is_list(A) andalso is_list(B) ->
|
611
|
+
equiv_arrays(A, B);
|
612
|
+
equiv(A, B) ->
|
613
|
+
A == B.
|
614
|
+
|
615
|
+
equiv_sorted_plists([], []) -> true;
|
616
|
+
equiv_sorted_plists([], _) -> false;
|
617
|
+
equiv_sorted_plists(_, []) -> false;
|
618
|
+
equiv_sorted_plists([{K1, V1} | R1], [{K2, V2} | R2]) ->
|
619
|
+
K1 == K2 andalso equiv(V1, V2) andalso equiv_sorted_plists(R1, R2).
|
620
|
+
|
621
|
+
equiv_arrays([], []) -> true;
|
622
|
+
equiv_arrays([], _) -> false;
|
623
|
+
equiv_arrays(_, []) -> false;
|
624
|
+
equiv_arrays([V1 | R1], [V2 | R2]) ->
|
625
|
+
equiv(V1, V2) andalso equiv_arrays(R1, R2).
|