jsone.erl 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. %%% @doc JSON decoding/encoding module
  2. %%% @end
  3. %%%
  4. %%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
  5. %%%
  6. %%% The MIT License
  7. %%%
  8. %%% Permission is hereby granted, free of charge, to any person obtaining a copy
  9. %%% of this software and associated documentation files (the "Software"), to deal
  10. %%% in the Software without restriction, including without limitation the rights
  11. %%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. %%% copies of the Software, and to permit persons to whom the Software is
  13. %%% furnished to do so, subject to the following conditions:
  14. %%%
  15. %%% The above copyright notice and this permission notice shall be included in
  16. %%% all copies or substantial portions of the Software.
  17. %%%
  18. %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. %%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. %%% THE SOFTWARE.
  25. %%%
  26. %%%---------------------------------------------------------------------------------------
  27. -module(jsone).
  28. %%--------------------------------------------------------------------------------
  29. %% Exported API
  30. %%--------------------------------------------------------------------------------
  31. -export([
  32. decode/1, decode/2,
  33. try_decode/1, try_decode/2,
  34. encode/1, encode/2,
  35. try_encode/1, try_encode/2
  36. ]).
  37. -export_type([
  38. json_value/0,
  39. json_boolean/0,
  40. json_number/0,
  41. json_string/0,
  42. json_array/0,
  43. json_object/0,
  44. json_object_members/0,
  45. json_object_format_tuple/0,
  46. json_object_format_proplist/0,
  47. json_object_format_map/0,
  48. encode_option/0,
  49. decode_option/0,
  50. float_format_option/0
  51. ]).
  52. %%--------------------------------------------------------------------------------
  53. %% Types & Macros
  54. %%--------------------------------------------------------------------------------
  55. -type json_value() :: json_number() | json_string() | json_array() | json_object() | json_boolean() | null.
  56. -type json_boolean() :: boolean().
  57. -type json_number() :: number().
  58. -type json_string() :: binary() | atom(). % NOTE: `decode/1' always returns `binary()' value
  59. -type json_array() :: [json_value()].
  60. -type json_object() :: json_object_format_tuple()
  61. | json_object_format_proplist()
  62. | json_object_format_map().
  63. -type json_object_members() :: [{json_string(), json_value()}].
  64. -type json_object_format_map() :: map().
  65. -type json_object_format_tuple() :: {json_object_members()}.
  66. -type json_object_format_proplist() :: [{}] | json_object_members().
  67. -type float_format_option() :: {scientific, Decimals :: 0..249}
  68. | {decimals, Decimals :: 0..253}
  69. | compact.
  70. %% `scientific': <br />
  71. %% - The float will be formatted using scientific notation with `Decimals' digits of precision. <br />
  72. %%
  73. %% `decimals': <br />
  74. %% - The encoded string will contain at most `Decimals' number of digits past the decimal point. <br />
  75. %% - If `compact' is provided the trailing zeros at the end of the string are truncated. <br />
  76. %%
  77. %% For more details, see <a href="http://erlang.org/doc/man/erlang.html#float_to_list-2">erlang:flaot_to_list/2</a>.
  78. %%
  79. %% ```
  80. %% > jsone:encode(1.23).
  81. %% <<"1.22999999999999998224e+00">>
  82. %%
  83. %% > jsone:encode(1.23, [{float_format, [{scientific, 4}]}]).
  84. %% <"1.2300e+00">>
  85. %%
  86. %% > jsone:encode(1.23, [{float_format, [{scientific, 1}]}]).
  87. %% <<"1.2e+00">>
  88. %%
  89. %% > jsone:encode(1.23, [{float_format, [{decimals, 4}]}]).
  90. %% <<"1.2300">>
  91. %%
  92. %% > jsone:encode(1.23, [{float_format, [{decimals, 4}, compact]}]).
  93. %% <<"1.23">>
  94. %%'''
  95. -type encode_option() :: native_utf8
  96. | {float_format, [float_format_option()]}
  97. | {space, non_neg_integer()}
  98. | {indent, non_neg_integer()}.
  99. %% `native_utf8': <br />
  100. %% - Encodes UTF-8 characters as a human-readable(non-escaped) string <br />
  101. %%
  102. %% `{float_format, Optoins}`:
  103. %% - Encodes a `float()` value in the format which specified by `Options' <br />
  104. %% - default: `[{scientific, 20}]' <br />
  105. %%
  106. %% `{space, N}': <br />
  107. %% - Inserts `N' spaces after every commna and colon <br />
  108. %% - default: `0' <br />
  109. %%
  110. %% `{indent, N}': <br />
  111. %% - Inserts a newline and `N' spaces for each level of indentation <br />
  112. %% - default: `0' <br />
  113. -type decode_option() :: {object_format, tuple | proplist | map}.
  114. %% object_format: <br />
  115. %% - Decoded JSON object format <br />
  116. %% - `tuple': An object is decoded as `{[]}' if it is empty, otherwise `{[{Key, Value}]}'. <br />
  117. %% - `proplist': An object is decoded as `[{}]' if it is empty, otherwise `[{Key, Value}]'. <br />
  118. %% - `map': An object is decoded as `#{}' if it is empty, otherwise `#{Key => Value}'. <br />
  119. %% - default: `map' <br />
  120. %%--------------------------------------------------------------------------------
  121. %% Exported Functions
  122. %%--------------------------------------------------------------------------------
  123. %% @equiv decode(Json, [])
  124. -spec decode(binary()) -> json_value().
  125. decode(Json) ->
  126. decode(Json, []).
  127. %% @doc Decodes an erlang term from json text (a utf8 encoded binary)
  128. %%
  129. %% Raises an error exception if input is not valid json
  130. %%
  131. %% ```
  132. %% > jsone:decode(<<"1">>, []).
  133. %% 1
  134. %%
  135. %% > jsone:decode(<<"wrong json">>, []).
  136. %% ** exception error: bad argument
  137. %% in function jsone_decode:number_integer_part/4
  138. %% called as jsone_decode:number_integer_part(<<"wrong json">>,1,[],<<>>)
  139. %% in call from jsone:decode/1 (src/jsone.erl, line 71)
  140. %% '''
  141. -spec decode(binary(), [decode_option()]) -> json_value().
  142. decode(Json, Options) ->
  143. try
  144. {ok, Value, _} = try_decode(Json, Options),
  145. Value
  146. catch
  147. error:{badmatch, {error, {Reason, [StackItem]}}} ->
  148. erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
  149. end.
  150. %% @equiv try_decode(Json, [])
  151. -spec try_decode(binary()) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
  152. try_decode(Json) ->
  153. try_decode(Json, []).
  154. %% @doc Decodes an erlang term from json text (a utf8 encoded binary)
  155. %%
  156. %% ```
  157. %% > jsone:try_decode(<<"[1,2,3] \"next value\"">>, []).
  158. %% {ok,[1,2,3],<<" \"next value\"">>}
  159. %%
  160. %% > jsone:try_decode(<<"wrong json">>, []).
  161. %% {error,{badarg,[{jsone_decode,number_integer_part,
  162. %% [<<"wrong json">>,1,[],<<>>],
  163. %% [{line,208}]}]}}
  164. %% '''
  165. -spec try_decode(binary(), [decode_option()]) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
  166. try_decode(Json, Options) ->
  167. jsone_decode:decode(Json, Options).
  168. %% @equiv encode(JsonValue, [])
  169. -spec encode(json_value()) -> binary().
  170. encode(JsonValue) ->
  171. encode(JsonValue, []).
  172. %% @doc Encodes an erlang term into json text (a utf8 encoded binary)
  173. %%
  174. %% Raises an error exception if input is not an instance of type `json_value()'
  175. %%
  176. %% ```
  177. %% > jsone:encode([1, null, 2]).
  178. %% <<"[1,null,2]">>
  179. %%
  180. %% > jsone:encode([1, hoge, 2]). % 'hoge' atom is not a json value
  181. %% ** exception error: bad argument
  182. %% in function jsone_encode:value/3
  183. %% called as jsone_encode:value(hoge,[{array_values,[2]}],<<"[1,">>)
  184. %% in call from jsone:encode/1 (src/jsone.erl, line 97)
  185. %% '''
  186. -spec encode(json_value(), [encode_option()]) -> binary().
  187. encode(JsonValue, Options) ->
  188. try
  189. {ok, Binary} = try_encode(JsonValue, Options),
  190. Binary
  191. catch
  192. error:{badmatch, {error, {Reason, [StackItem]}}} ->
  193. erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
  194. end.
  195. %% @equiv try_encode(JsonValue, [])
  196. -spec try_encode(json_value()) -> {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
  197. try_encode(JsonValue) ->
  198. try_encode(JsonValue, []).
  199. %% @doc Encodes an erlang term into json text (a utf8 encoded binary)
  200. %%
  201. %% ```
  202. %% > jsone:try_encode([1, null, 2]).
  203. %% {ok,<<"[1,null,2]">>}
  204. %%
  205. %% > jsone:try_encode([1, hoge, 2]). % 'hoge' atom is not a json value
  206. %% {error,{badarg,[{jsone_encode,value,
  207. %% [hoge,[{array_values,[2]}],<<"[1,">>],
  208. %% [{line,86}]}]}}
  209. %% '''
  210. -spec try_encode(json_value(), [encode_option()]) -> {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
  211. try_encode(JsonValue, Options) ->
  212. jsone_encode:encode(JsonValue, Options).