Просмотр исходного кода

Merge pull request #2 from sile/experimental

experimentalブランチの取り込み
Takeru Ohta 10 лет назад
Родитель
Сommit
161c08d32f
8 измененных файлов с 359 добавлено и 232 удалено
  1. 1 1
      COPYING
  2. 26 14
      README.md
  3. 75 21
      doc/jsone.md
  4. 41 19
      src/jsone.erl
  5. 155 133
      src/jsone_decode.erl
  6. 34 31
      src/jsone_encode.erl
  7. 9 7
      test/jsone_decode_tests.erl
  8. 18 6
      test/jsone_encode_tests.erl

+ 1 - 1
COPYING

@@ -1,6 +1,6 @@
 The MIT License
 
-Copyright (c) 2013-2014 Takeru Ohta <phjgt308@gmail.com>
+Copyright (c) 2013-2015 Takeru Ohta <phjgt308@gmail.com>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 26 - 14
README.md

@@ -1,4 +1,4 @@
-jsone (0.2.4)
+jsone (0.3.0)
 =============
 
 An Erlang library for encoding, decoding [JSON](http://json.org/index.html) data.
@@ -80,17 +80,20 @@ Usage Example
 > jsone:encode({[{<<"key">>, <<"value">>}]}).
 <<"{\"key\":\"value\"}">>
 
+> jsone:encode({[{key, <<"value">>}]}). % atom key is allowed
+<<"{\"key\":\"value\"}">>
+
 % error: raises exception
-> jsone:encode({[{key, <<"value">>}]}). % non binary key is not allowed
+> jsone:encode({[{123, <<"value">>}]}). % non binary|atom key is not allowed
 ** exception error: bad argument
      in function  jsone_encode:object_members/3
-        called as jsone_encode:object_members([{key,<<"value">>}],[],<<"{">>)
+        called as jsone_encode:object_members([{123,<<"value">>}],[],<<"{">>)
      in call from jsone:encode/1 (src/jsone.erl, line 97)
 
 % error: returns {error, Reason}
-> jsone:try_encode({[{key, <<"value">>}]}).
+> jsone:try_encode({[{123, <<"value">>}]}).
 {error,{badarg,[{jsone_encode,object_members,
-                              [[{key,<<"value">>}],[],<<"{">>],
+                              [[{123,<<"value">>}],[],<<"{">>],
                               [{line,138}]}]}}
 ```
 
@@ -98,18 +101,27 @@ Usage Example
 Data Mapping (Erlang <=> JSON)
 -------------------------------
 
-|         | Erlang                       | JSON            |
-|:-------:|-----------------------------:|----------------:|
-| number  |                          123 |             123 |
-| null    |                         null |            null |
-| boolean |                         true |            true |
-| string  |                    <<"abc">> |           "abc" |
-| array   |                      [1,2,3] |         [1,2,3] |
-| object  | {[{<<"key">>, <<"value">>}]} | {"key":"value"} |
-
+```
+Erlang                  JSON             Erlang
+=================================================================================================
+
+null                 -> null          -> null
+true                 -> true          -> true
+false                -> false         -> false
+<<"abc">>            -> "abc"         -> <<"abc">>
+abc                  -> "abc"         -> <<"abc">> % non-special atom is regarded as a binary
+123                  -> 123           -> 123
+123.4                -> 123.4         -> 123.4
+[1,2,3]              -> [1,2,3]       -> [1,2,3]
+{[]}                 -> {}            -> {[]}                       % object_format=tuple
+{[{key, <<"val">>}]} -> {"key":"val"} -> {[{<<"key">>, <<"val">>}]} % object_format=tuple
+[{}]                 -> {}            -> [{}]                       % object_format=proplist
+[{<<"key">>, val}]   -> {"key":"val"} -> [{<<"key">>, <<"val">>}]   % object_format=proplist
+```
 
 API
 ---
+
 See [EDoc Document](doc/jsone.md)
 
 

+ 75 - 21
doc/jsone.md

@@ -17,6 +17,24 @@ JSON decoding/encoding module.
 
 
 
+### <a name="type-decode_option">decode_option()</a> ###
+
+
+
+<pre><code>
+decode_option() = {object_format, tuple | proplist}
+</code></pre>
+
+
+
+  object_format: <br />
+- Decoded JSON object format <br />
+- `tuple`: An object is decoded as `{[]}` if it is empty, otherwise `{[{Key, Value}]}`. <br />
+- `proplist`: An object is decoded as `[{}]` if it is empty, otherwise `[{Key, Value}]`. <br />
+- default: `tuple` <br />
+
+
+
 ### <a name="type-encode_option">encode_option()</a> ###
 
 
@@ -72,7 +90,31 @@ json_number() = number()
 
 
 <pre><code>
-json_object() = {<a href="#type-json_object_members">json_object_members()</a>}
+json_object() = <a href="#type-json_object_format_tuple">json_object_format_tuple()</a> | <a href="#type-json_object_format_proplist">json_object_format_proplist()</a>
+</code></pre>
+
+
+
+
+
+### <a name="type-json_object_format_proplist">json_object_format_proplist()</a> ###
+
+
+
+<pre><code>
+json_object_format_proplist() = [{}] | <a href="#type-json_object_members">json_object_members()</a>
+</code></pre>
+
+
+
+
+
+### <a name="type-json_object_format_tuple">json_object_format_tuple()</a> ###
+
+
+
+<pre><code>
+json_object_format_tuple() = {<a href="#type-json_object_members">json_object_members()</a>}
 </code></pre>
 
 
@@ -96,11 +138,13 @@ json_object_members() = [{<a href="#type-json_string">json_string()</a>, <a href
 
 
 <pre><code>
-json_string() = binary()
+json_string() = binary() | atom()
 </code></pre>
 
 
 
+ NOTE: `decode/1` always returns `binary()` value
+
 
 
 ### <a name="type-json_value">json_value()</a> ###
@@ -117,7 +161,7 @@ json_value() = <a href="#type-json_number">json_number()</a> | <a href="#type-js
 ## Function Index ##
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#decode-1">decode/1</a></td><td>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#encode-1">encode/1</a></td><td>Equivalent to <a href="#encode-2"><tt>encode(JsonValue, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#encode-2">encode/2</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_decode-1">try_decode/1</a></td><td>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_encode-1">try_encode/1</a></td><td>Equivalent to <a href="#try_encode-2"><tt>try_encode(JsonValue, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#try_encode-2">try_encode/2</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#decode-1">decode/1</a></td><td>Equivalent to <a href="#decode-2"><tt>decode(Json, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#decode-2">decode/2</a></td><td>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#encode-1">encode/1</a></td><td>Equivalent to <a href="#encode-2"><tt>encode(JsonValue, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#encode-2">encode/2</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_decode-1">try_decode/1</a></td><td>Equivalent to <a href="#try_decode-2"><tt>try_decode(Json, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#try_decode-2">try_decode/2</a></td><td>Decodes an erlang term from json text (a utf8 encoded binary).</td></tr><tr><td valign="top"><a href="#try_encode-1">try_encode/1</a></td><td>Equivalent to <a href="#try_encode-2"><tt>try_encode(JsonValue, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#try_encode-2">try_encode/2</a></td><td>Encodes an erlang term into json text (a utf8 encoded binary).</td></tr></table>
 
 
 <a name="functions"></a>
@@ -132,10 +176,19 @@ json_value() = <a href="#type-json_number">json_number()</a> | <a href="#type-js
 <pre><code>
 decode(Json::binary()) -&gt; <a href="#type-json_value">json_value()</a>
 </code></pre>
+<br />
+
+Equivalent to [`decode(Json, [])`](#decode-2).
+<a name="decode-2"></a>
 
-<br></br>
+### decode/2 ###
 
 
+<pre><code>
+decode(Json::binary(), Options::[<a href="#type-decode_option">decode_option()</a>]) -&gt; <a href="#type-json_value">json_value()</a>
+</code></pre>
+<br />
+
 
 Decodes an erlang term from json text (a utf8 encoded binary)
 
@@ -146,9 +199,9 @@ Raises an error exception if input is not valid json
 
 
 ```
-  > jsone:decode(<<"1">>).
+  > jsone:decode(<<"1">>, []).
   1
-  > jsone:decode(<<"wrong json">>).
+  > jsone:decode(<<"wrong json">>, []).
   ** exception error: bad argument
       in function  jsone_decode:number_integer_part/4
          called as jsone_decode:number_integer_part(<<"wrong json">>,1,[],<<>>)
@@ -163,9 +216,7 @@ Raises an error exception if input is not valid json
 <pre><code>
 encode(JsonValue::<a href="#type-json_value">json_value()</a>) -&gt; binary()
 </code></pre>
-
-<br></br>
-
+<br />
 
 Equivalent to [`encode(JsonValue, [])`](#encode-2).
 <a name="encode-2"></a>
@@ -176,9 +227,7 @@ Equivalent to [`encode(JsonValue, [])`](#encode-2).
 <pre><code>
 encode(JsonValue::<a href="#type-json_value">json_value()</a>, Options::[<a href="#type-encode_option">encode_option()</a>]) -&gt; binary()
 </code></pre>
-
-<br></br>
-
+<br />
 
 
 Encodes an erlang term into json text (a utf8 encoded binary)
@@ -207,9 +256,18 @@ Raises an error exception if input is not an instance of type `json_value()`
 <pre><code>
 try_decode(Json::binary()) -&gt; {ok, <a href="#type-json_value">json_value()</a>, Remainings::binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
 </code></pre>
+<br />
+
+Equivalent to [`try_decode(Json, [])`](#try_decode-2).
+<a name="try_decode-2"></a>
+
+### try_decode/2 ###
 
-<br></br>
 
+<pre><code>
+try_decode(Json::binary(), Options::[<a href="#type-decode_option">decode_option()</a>]) -&gt; {ok, <a href="#type-json_value">json_value()</a>, Remainings::binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
+</code></pre>
+<br />
 
 
 Decodes an erlang term from json text (a utf8 encoded binary)
@@ -217,9 +275,9 @@ Decodes an erlang term from json text (a utf8 encoded binary)
 
 
 ```
-  > jsone:try_decode(<<"[1,2,3] \"next value\"">>).
+  > jsone:try_decode(<<"[1,2,3] \"next value\"">>, []).
   {ok,[1,2,3],<<" \"next value\"">>}
-  > jsone:try_decode(<<"wrong json">>).
+  > jsone:try_decode(<<"wrong json">>, []).
   {error,{badarg,[{jsone_decode,number_integer_part,
                                 [<<"wrong json">>,1,[],<<>>],
                                 [{line,208}]}]}}
@@ -233,9 +291,7 @@ Decodes an erlang term from json text (a utf8 encoded binary)
 <pre><code>
 try_encode(JsonValue::<a href="#type-json_value">json_value()</a>) -&gt; {ok, binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
 </code></pre>
-
-<br></br>
-
+<br />
 
 Equivalent to [`try_encode(JsonValue, [])`](#try_encode-2).
 <a name="try_encode-2"></a>
@@ -246,9 +302,7 @@ Equivalent to [`try_encode(JsonValue, [])`](#try_encode-2).
 <pre><code>
 try_encode(JsonValue::<a href="#type-json_value">json_value()</a>, Options::[<a href="#type-encode_option">encode_option()</a>]) -&gt; {ok, binary()} | {error, {Reason::term(), [<a href="erlang.md#type-stack_item">erlang:stack_item()</a>]}}
 </code></pre>
-
-<br></br>
-
+<br />
 
 
 Encodes an erlang term into json text (a utf8 encoded binary)

+ 41 - 19
src/jsone.erl

@@ -1,7 +1,7 @@
 %%% @doc JSON decoding/encoding module
 %%% @end
 %%%
-%%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
 %%%
 %%% The MIT License
 %%%
@@ -30,21 +30,25 @@
 %% Exported API
 %%--------------------------------------------------------------------------------
 -export([
-         decode/1, try_decode/1,
+         decode/1, decode/2,
+         try_decode/1, try_decode/2,
          encode/1, encode/2,
          try_encode/1, try_encode/2
         ]).
 
 -export_type([
               json_value/0,
+              json_boolean/0,
               json_number/0,
               json_string/0,
               json_array/0,
               json_object/0,
               json_object_members/0,
-              json_boolean/0,
+              json_object_format_tuple/0,
+              json_object_format_proplist/0,
 
-              encode_option/0
+              encode_option/0,
+              decode_option/0
              ]).
 
 %%--------------------------------------------------------------------------------
@@ -53,62 +57,80 @@
 -type json_value()          :: json_number() | json_string() | json_array() | json_object() | json_boolean() | null.
 -type json_boolean()        :: boolean().
 -type json_number()         :: number().
--type json_string()         :: binary().
+-type json_string()         :: binary() | atom(). % NOTE: `decode/1' always returns `binary()' value
 -type json_array()          :: [json_value()].
--type json_object()         :: {json_object_members()}.
+-type json_object()         :: json_object_format_tuple() | json_object_format_proplist().
 -type json_object_members() :: [{json_string(), json_value()}].
 
+-type json_object_format_tuple() :: {json_object_members()}.
+-type json_object_format_proplist() :: [{}] | json_object_members().
+
 -type encode_option() :: native_utf8.
 %% native_utf8: Encodes UTF-8 characters as a human-readable(non-escaped) string
 
--define(DEFAULT_ENCODE_OPTIONS, []).
+-type decode_option() :: {object_format, tuple | proplist}.
+%% object_format: <br />
+%%  - Decoded JSON object format <br />
+%%  - `tuple': An object is decoded as `{[]}' if it is empty, otherwise `{[{Key, Value}]}'. <br />
+%%  - `proplist': An object is decoded as `[{}]' if it is empty, otherwise `[{Key, Value}]'. <br />
+%%  - default: `tuple' <br />
 
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
+%% @equiv decode(Json, [])
+-spec decode(binary()) -> json_value().
+decode(Json) ->
+    decode(Json, []).
+
 %% @doc Decodes an erlang term from json text (a utf8 encoded binary)
 %%
 %% Raises an error exception if input is not valid json
 %%
 %% ```
-%% > jsone:decode(<<"1">>).
+%% > jsone:decode(<<"1">>, []).
 %% 1
 %%
-%% > jsone:decode(<<"wrong json">>).
+%% > jsone:decode(<<"wrong json">>, []).
 %% ** exception error: bad argument
 %%     in function  jsone_decode:number_integer_part/4
 %%        called as jsone_decode:number_integer_part(<<"wrong json">>,1,[],<<>>)
 %%     in call from jsone:decode/1 (src/jsone.erl, line 71)
 %% '''
--spec decode(binary()) -> json_value().
-decode(Json) ->
+-spec decode(binary(), [decode_option()]) -> json_value().
+decode(Json, Options) ->
     try
-        {ok, Value, _} = try_decode(Json),
+        {ok, Value, _} = try_decode(Json, Options),
         Value
     catch
         error:{badmatch, {error, {Reason, [StackItem]}}} ->
             erlang:raise(error, Reason, [StackItem | erlang:get_stacktrace()])
     end.
 
+%% @equiv try_decode(Json, [])
+-spec try_decode(binary()) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
+try_decode(Json) ->
+    try_decode(Json, []).
+
 %% @doc Decodes an erlang term from json text (a utf8 encoded binary)
 %%
 %% ```
-%% > jsone:try_decode(<<"[1,2,3] \"next value\"">>).
+%% > jsone:try_decode(<<"[1,2,3] \"next value\"">>, []).
 %% {ok,[1,2,3],<<" \"next value\"">>}
 %%
-%% > jsone:try_decode(<<"wrong json">>).
+%% > jsone:try_decode(<<"wrong json">>, []).
 %% {error,{badarg,[{jsone_decode,number_integer_part,
 %%                               [<<"wrong json">>,1,[],<<>>],
 %%                               [{line,208}]}]}}
 %% '''
--spec try_decode(binary()) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
-try_decode(Json) ->
-    jsone_decode:decode(Json).
+-spec try_decode(binary(), [decode_option()]) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
+try_decode(Json, Options) ->
+    jsone_decode:decode(Json, Options).
 
 %% @equiv encode(JsonValue, [])
 -spec encode(json_value()) -> binary().
 encode(JsonValue) ->
-    encode(JsonValue, ?DEFAULT_ENCODE_OPTIONS).
+    encode(JsonValue, []).
 
 %% @doc Encodes an erlang term into json text (a utf8 encoded binary)
 %%
@@ -137,7 +159,7 @@ encode(JsonValue, Options) ->
 %% @equiv try_encode(JsonValue, [])
 -spec try_encode(json_value()) -> {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
 try_encode(JsonValue) ->
-    jsone_encode:encode(JsonValue, ?DEFAULT_ENCODE_OPTIONS).
+    try_encode(JsonValue, []).
 
 %% @doc Encodes an erlang term into json text (a utf8 encoded binary)
 %%

+ 155 - 133
src/jsone_decode.erl

@@ -2,7 +2,7 @@
 %%% @private
 %%% @end
 %%%
-%%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
 %%%
 %%% The MIT License
 %%%
@@ -30,10 +30,10 @@
 %%--------------------------------------------------------------------------------
 %% Exported API
 %%--------------------------------------------------------------------------------
--export([decode/1]).
+-export([decode/1, decode/2]).
 
 %%--------------------------------------------------------------------------------
-%% Macros & Types
+%% Macros & Records & Types
 %%--------------------------------------------------------------------------------
 -define(ERROR(Function, Args), {error, {badarg, [{?MODULE, Function, Args, [{line, ?LINE}]}]}}).
 
@@ -51,110 +51,118 @@
 
 -type decode_result() :: {ok, jsone:json_value(), Rest::binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
 
+-record(decode_opt_v1, { object_format=tuple :: tuple | proplist}).
+-define(OPT, #decode_opt_v1).
+-type opt() :: #decode_opt_v1{}.
+
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
-%% @doc Decodes an erlang term from json text (a utf8 encoded binary)
 -spec decode(binary()) -> decode_result().
-decode(<<Json/binary>>) ->
-    whitespace(Json, value, [], <<"">>).
+decode(Json) ->
+    decode(Json, []).
+
+-spec decode(binary(), [jsone:decode_option()]) -> decode_result().
+decode(<<Json/binary>>, Options) ->
+    Opt = parse_options(Options),
+    whitespace(Json, value, [], <<"">>, Opt).
 
 %%--------------------------------------------------------------------------------
 %% Internal Functions
 %%--------------------------------------------------------------------------------
--spec next(binary(), jsone:json_value(), [next()], binary()) -> decode_result().
-next(<<Bin/binary>>, Value, [], _Buf) ->
+-spec next(binary(), jsone:json_value(), [next()], binary(), opt()) -> decode_result().
+next(<<Bin/binary>>, Value, [], _Buf, _Opt) ->
     {ok, Value, Bin};
-next(<<Bin/binary>>, Value, [Next | Nexts], Buf) ->
+next(<<Bin/binary>>, Value, [Next | Nexts], Buf, Opt) ->
     case Next of
-        {array_next, Values}        -> whitespace(Bin, {array_next, [Value | Values]}, Nexts, Buf);
-        {object_value, Members}     -> whitespace(Bin, {object_value, Value, Members}, Nexts, Buf);
-        {object_next, Key, Members} -> whitespace(Bin, {object_next, [{Key, Value} | Members]}, Nexts, Buf)
+        {array_next, Values}        -> whitespace(Bin, {array_next, [Value | Values]}, Nexts, Buf, Opt);
+        {object_value, Members}     -> whitespace(Bin, {object_value, Value, Members}, Nexts, Buf, Opt);
+        {object_next, Key, Members} -> whitespace(Bin, {object_next, [{Key, Value} | Members]}, Nexts, Buf, Opt)
     end.
 
--spec whitespace(binary(), whitespace_next(), [next()], binary()) -> decode_result().
-whitespace(<<$  , Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
-whitespace(<<$\t, Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
-whitespace(<<$\r, Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
-whitespace(<<$\n, Bin/binary>>, Next, Nexts, Buf) -> whitespace(Bin, Next, Nexts, Buf);
-whitespace(<<Bin/binary>>,      Next, Nexts, Buf) ->
+-spec whitespace(binary(), whitespace_next(), [next()], binary(), opt()) -> decode_result().
+whitespace(<<$  , Bin/binary>>, Next, Nexts, Buf, Opt) -> whitespace(Bin, Next, Nexts, Buf, Opt);
+whitespace(<<$\t, Bin/binary>>, Next, Nexts, Buf, Opt) -> whitespace(Bin, Next, Nexts, Buf, Opt);
+whitespace(<<$\r, Bin/binary>>, Next, Nexts, Buf, Opt) -> whitespace(Bin, Next, Nexts, Buf, Opt);
+whitespace(<<$\n, Bin/binary>>, Next, Nexts, Buf, Opt) -> whitespace(Bin, Next, Nexts, Buf, Opt);
+whitespace(<<Bin/binary>>,      Next, Nexts, Buf, Opt) ->
     case Next of
-        value  -> value(Bin, Nexts, Buf);
-        array  -> array(Bin, Nexts, Buf);
-        object -> object(Bin, Nexts, Buf);
-        {object_key, Members}        -> object_key(Bin, Members, Nexts, Buf);
-        {array_next, Values}         -> array_next(Bin, Values, Nexts, Buf);
-        {object_value, Key, Members} -> object_value(Bin, Key, Members, Nexts, Buf);
-        {object_next, Members}       -> object_next(Bin, Members, Nexts, Buf)
+        value  -> value(Bin, Nexts, Buf, Opt);
+        array  -> array(Bin, Nexts, Buf, Opt);
+        object -> object(Bin, Nexts, Buf, Opt);
+        {object_key, Members}        -> object_key(Bin, Members, Nexts, Buf, Opt);
+        {array_next, Values}         -> array_next(Bin, Values, Nexts, Buf, Opt);
+        {object_value, Key, Members} -> object_value(Bin, Key, Members, Nexts, Buf, Opt);
+        {object_next, Members}       -> object_next(Bin, Members, Nexts, Buf, Opt)
     end.
 
--spec value(binary(), [next()], binary()) -> decode_result().
-value(<<"false", Bin/binary>>, Nexts, Buf) -> next(Bin, false, Nexts, Buf);
-value(<<"true", Bin/binary>>, Nexts, Buf)  -> next(Bin, true, Nexts, Buf);
-value(<<"null", Bin/binary>>, Nexts, Buf)  -> next(Bin, null, Nexts, Buf);
-value(<<$[, Bin/binary>>, Nexts, Buf)      -> whitespace(Bin, array, Nexts, Buf);
-value(<<${, Bin/binary>>, Nexts, Buf)      -> whitespace(Bin, object, Nexts, Buf);
-value(<<$", Bin/binary>>, Nexts, Buf)      -> string(Bin, byte_size(Buf), Nexts, Buf);
-value(<<Bin/binary>>, Nexts, Buf)          -> number(Bin, Nexts, Buf).
+-spec value(binary(), [next()], binary(), opt()) -> decode_result().
+value(<<"false", Bin/binary>>, Nexts, Buf, Opt) -> next(Bin, false, Nexts, Buf, Opt);
+value(<<"true", Bin/binary>>, Nexts, Buf, Opt)  -> next(Bin, true, Nexts, Buf, Opt);
+value(<<"null", Bin/binary>>, Nexts, Buf, Opt)  -> next(Bin, null, Nexts, Buf, Opt);
+value(<<$[, Bin/binary>>, Nexts, Buf, Opt)      -> whitespace(Bin, array, Nexts, Buf, Opt);
+value(<<${, Bin/binary>>, Nexts, Buf, Opt)      -> whitespace(Bin, object, Nexts, Buf, Opt);
+value(<<$", Bin/binary>>, Nexts, Buf, Opt)      -> string(Bin, byte_size(Buf), Nexts, Buf, Opt);
+value(<<Bin/binary>>, Nexts, Buf, Opt)          -> number(Bin, Nexts, Buf, Opt).
 
--spec array(binary(), [next()], binary()) -> decode_result().
-array(<<$], Bin/binary>>, Nexts, Buf) -> next(Bin, [], Nexts, Buf);
-array(<<Bin/binary>>, Nexts, Buf)     -> value(Bin, [{array_next, []} | Nexts], Buf).
+-spec array(binary(), [next()], binary(), opt()) -> decode_result().
+array(<<$], Bin/binary>>, Nexts, Buf, Opt) -> next(Bin, [], Nexts, Buf, Opt);
+array(<<Bin/binary>>, Nexts, Buf, Opt)     -> value(Bin, [{array_next, []} | Nexts], Buf, Opt).
 
--spec array_next(binary(), [jsone:json_value()], [next()], binary()) -> decode_result().
-array_next(<<$], Bin/binary>>, Values, Nexts, Buf) -> next(Bin, lists:reverse(Values), Nexts, Buf);
-array_next(<<$,, Bin/binary>>, Values, Nexts, Buf) -> whitespace(Bin, value, [{array_next, Values} | Nexts], Buf);
-array_next(Bin,                Values, Nexts, Buf) -> ?ERROR(array_next, [Bin, Values, Nexts, Buf]).
+-spec array_next(binary(), [jsone:json_value()], [next()], binary(), opt()) -> decode_result().
+array_next(<<$], Bin/binary>>, Values, Nexts, Buf, Opt) -> next(Bin, lists:reverse(Values), Nexts, Buf, Opt);
+array_next(<<$,, Bin/binary>>, Values, Nexts, Buf, Opt) -> whitespace(Bin, value, [{array_next, Values} | Nexts], Buf, Opt);
+array_next(Bin,                Values, Nexts, Buf, Opt) -> ?ERROR(array_next, [Bin, Values, Nexts, Buf, Opt]).
 
--spec object(binary(), [next()], binary()) -> decode_result().
-object(<<$}, Bin/binary>>, Nexts, Buf) -> next(Bin, {[]}, Nexts, Buf);
-object(<<Bin/binary>>, Nexts, Buf)     -> object_key(Bin, [], Nexts, Buf).
+-spec object(binary(), [next()], binary(), opt()) -> decode_result().
+object(<<$}, Bin/binary>>, Nexts, Buf, Opt) -> next(Bin, make_object([], Opt), Nexts, Buf, Opt);
+object(<<Bin/binary>>, Nexts, Buf, Opt)     -> object_key(Bin, [], Nexts, Buf, Opt).
 
--spec object_key(binary(), jsone:json_object_members(), [next()], binary()) -> decode_result().
-object_key(<<$", Bin/binary>>, Members, Nexts, Buf) -> string(Bin, byte_size(Buf), [{object_value, Members} | Nexts], Buf);
-object_key(<<Bin/binary>>, Members, Nexts, Buf)     -> ?ERROR(object_key, [Bin, Members, Nexts, Buf]).
+-spec object_key(binary(), jsone:json_object_members(), [next()], binary(), opt()) -> decode_result().
+object_key(<<$", Bin/binary>>, Members, Nexts, Buf, Opt) -> string(Bin, byte_size(Buf), [{object_value, Members} | Nexts], Buf, Opt);
+object_key(<<Bin/binary>>, Members, Nexts, Buf, Opt)     -> ?ERROR(object_key, [Bin, Members, Nexts, Buf, Opt]).
 
--spec object_value(binary(), jsone:json_string(), jsone:json_object_members(), [next()], binary()) -> decode_result().
-object_value(<<$:, Bin/binary>>, Key, Members, Nexts, Buf) -> whitespace(Bin, value, [{object_next, Key, Members} | Nexts], Buf);
-object_value(Bin,                Key, Members, Nexts, Buf) -> ?ERROR(object_value, [Bin, Key, Members, Nexts, Buf]).
+-spec object_value(binary(), jsone:json_string(), jsone:json_object_members(), [next()], binary(), opt()) -> decode_result().
+object_value(<<$:, Bin/binary>>, Key, Members, Nexts, Buf, Opt) -> whitespace(Bin, value, [{object_next, Key, Members} | Nexts], Buf, Opt);
+object_value(Bin,                Key, Members, Nexts, Buf, Opt) -> ?ERROR(object_value, [Bin, Key, Members, Nexts, Buf, Opt]).
 
--spec object_next(binary(), jsone:json_object_members(), [next()], binary()) -> decode_result().
-object_next(<<$}, Bin/binary>>, Members, Nexts, Buf) -> next(Bin, {Members}, Nexts, Buf);
-object_next(<<$,, Bin/binary>>, Members, Nexts, Buf) -> whitespace(Bin, {object_key, Members}, Nexts, Buf);
-object_next(Bin,                Members, Nexts, Buf) -> ?ERROR(object_next, [Bin, Members, Nexts, Buf]).
+-spec object_next(binary(), jsone:json_object_members(), [next()], binary(), opt()) -> decode_result().
+object_next(<<$}, Bin/binary>>, Members, Nexts, Buf, Opt) -> next(Bin, make_object(lists:reverse(Members), Opt), Nexts, Buf, Opt);
+object_next(<<$,, Bin/binary>>, Members, Nexts, Buf, Opt) -> whitespace(Bin, {object_key, Members}, Nexts, Buf, Opt);
+object_next(Bin,                Members, Nexts, Buf, Opt) -> ?ERROR(object_next, [Bin, Members, Nexts, Buf, Opt]).
 
--spec string(binary(), non_neg_integer(), [next()], binary()) -> decode_result().
-string(<<Bin/binary>>, Start, Nexts, Buf) ->
-    string(Bin, Bin, Start, Nexts, Buf).
+-spec string(binary(), non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+string(<<Bin/binary>>, Start, Nexts, Buf, Opt) ->
+    string(Bin, Bin, Start, Nexts, Buf, Opt).
 
--spec string(binary(), binary(), non_neg_integer(), [next()], binary()) -> decode_result().
-string(<<$", Bin/binary>>, Base, Start, Nexts, Buf) ->
+-spec string(binary(), binary(), non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+string(<<$", Bin/binary>>, Base, Start, Nexts, Buf, Opt) ->
     Prefix = binary:part(Base, 0, byte_size(Base) - byte_size(Bin) - 1),
     case Start =:= byte_size(Buf) of
-        true  -> next(Bin, Prefix, Nexts, Buf);
+        true  -> next(Bin, Prefix, Nexts, Buf, Opt);
         false ->
             Buf2 = <<Buf/binary, Prefix/binary>>,
-            next(Bin, binary:part(Buf2, Start, byte_size(Buf2) - Start), Nexts, Buf2)
+            next(Bin, binary:part(Buf2, Start, byte_size(Buf2) - Start), Nexts, Buf2, Opt)
     end;
-string(<<$\\, B/binary>>, Base, Start, Nexts, Buf) ->
+string(<<$\\, B/binary>>, Base, Start, Nexts, Buf, Opt) ->
     Prefix = binary:part(Base, 0, byte_size(Base) - byte_size(B) - 1),
     case B of
-        <<$", Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $">>);
-        <<$/, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $/>>);
-        <<$\\,Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\\>>);
-        <<$b, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\b>>);
-        <<$f, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\f>>);
-        <<$n, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\n>>);
-        <<$r, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\r>>);
-        <<$t, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\t>>);
-        <<$u, Bin/binary>> -> unicode_string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary>>);
-        _                  -> ?ERROR(string, [<<$\\, B/binary>>, Base, Start, Nexts, Buf])
+        <<$", Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $">>, Opt);
+        <<$/, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $/>>, Opt);
+        <<$\\,Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\\>>, Opt);
+        <<$b, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\b>>, Opt);
+        <<$f, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\f>>, Opt);
+        <<$n, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\n>>, Opt);
+        <<$r, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\r>>, Opt);
+        <<$t, Bin/binary>> -> string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary, $\t>>, Opt);
+        <<$u, Bin/binary>> -> unicode_string(Bin, Start, Nexts, <<Buf/binary, Prefix/binary>>, Opt);
+        _                  -> ?ERROR(string, [<<$\\, B/binary>>, Base, Start, Nexts, Buf, Opt])
     end;
-string(<<C, Bin/binary>>, Base, Start, Nexts, Buf) when 16#20 =< C ->
-    string(Bin, Base, Start, Nexts, Buf).
+string(<<C, Bin/binary>>, Base, Start, Nexts, Buf, Opt) when 16#20 =< C ->
+    string(Bin, Base, Start, Nexts, Buf, Opt).
 
--spec unicode_string(binary(), non_neg_integer(), [next()], binary()) -> decode_result().
-unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf) ->
+-spec unicode_string(binary(), non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf, Opt) ->
     case binary_to_integer(N, 16) of
         High when 16#D800 =< High, High =< 16#DBFF ->
             %% surrogate pair
@@ -163,18 +171,18 @@ unicode_string(<<N:4/binary, Bin/binary>>, Start, Nexts, Buf) ->
                     case binary_to_integer(N2, 16) of
                         Low when 16#DC00 =< Low, Low =< 16#DFFF ->
                             Unicode = 16#10000 + (High - 16#D800) * 16#400 + (Low - 16#DC00),
-                            string(Bin2, Start, Nexts, unicode_to_utf8(Unicode, Buf));
-                        _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
+                            string(Bin2, Start, Nexts, unicode_to_utf8(Unicode, Buf), Opt);
+                        _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf, Opt])
                     end;
-                _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf])
+                _ -> ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf, Opt])
             end;
         Unicode when 16#DC00 =< Unicode, Unicode =< 16#DFFF ->  % second part of surrogate pair (without first part)
-            ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf]);
-        Unicode -> 
-            string(Bin, Start, Nexts, unicode_to_utf8(Unicode, Buf))
+            ?ERROR(unicode_string, [<<N/binary, Bin/binary>>, Start, Nexts, Buf, Opt]);
+        Unicode ->
+            string(Bin, Start, Nexts, unicode_to_utf8(Unicode, Buf), Opt)
     end;
-unicode_string(Bin, Start, Nexts, Buf) ->
-    ?ERROR(unicode_string, [Bin, Start, Nexts, Buf]).
+unicode_string(Bin, Start, Nexts, Buf, Opt) ->
+    ?ERROR(unicode_string, [Bin, Start, Nexts, Buf, Opt]).
 
 -spec unicode_to_utf8(0..1114111, binary()) -> binary().
 unicode_to_utf8(Code, Buf) when Code < 16#80 ->
@@ -195,62 +203,76 @@ unicode_to_utf8(Code, Buf) ->
     D = 2#10000000 bor (Code band 2#111111),
     <<Buf/binary, A, B, C, D>>.
 
--spec number(binary(), [next()], binary()) -> decode_result().
-number(<<$-, Bin/binary>>, Nexts, Buf) -> number_integer_part(Bin, -1, Nexts, Buf);
-number(<<Bin/binary>>,     Nexts, Buf) -> number_integer_part(Bin,  1, Nexts, Buf).
+-spec number(binary(), [next()], binary(), opt()) -> decode_result().
+number(<<$-, Bin/binary>>, Nexts, Buf, Opt) -> number_integer_part(Bin, -1, Nexts, Buf, Opt);
+number(<<Bin/binary>>,     Nexts, Buf, Opt) -> number_integer_part(Bin,  1, Nexts, Buf, Opt).
 
--spec number_integer_part(binary(), 1|-1, [next()], binary()) -> decode_result().
-number_integer_part(<<$0, Bin/binary>>, Sign, Nexts, Buf) ->
-    number_fraction_part(Bin, Sign, 0, Nexts, Buf);
-number_integer_part(<<C, Bin/binary>>, Sign, Nexts, Buf) when $1 =< C, C =< $9 ->
-    number_integer_part_rest(Bin, C - $0, Sign, Nexts, Buf);
-number_integer_part(Bin, Sign, Nexts, Buf) ->
-    ?ERROR(number_integer_part, [Bin, Sign, Nexts, Buf]).
+-spec number_integer_part(binary(), 1|-1, [next()], binary(), opt()) -> decode_result().
+number_integer_part(<<$0, Bin/binary>>, Sign, Nexts, Buf, Opt) ->
+    number_fraction_part(Bin, Sign, 0, Nexts, Buf, Opt);
+number_integer_part(<<C, Bin/binary>>, Sign, Nexts, Buf, Opt) when $1 =< C, C =< $9 ->
+    number_integer_part_rest(Bin, C - $0, Sign, Nexts, Buf, Opt);
+number_integer_part(Bin, Sign, Nexts, Buf, Opt) ->
+    ?ERROR(number_integer_part, [Bin, Sign, Nexts, Buf, Opt]).
 
--spec number_integer_part_rest(binary(), non_neg_integer(), 1|-1, [next()], binary()) -> decode_result().
-number_integer_part_rest(<<C, Bin/binary>>, N, Sign, Nexts, Buf) when $0 =< C, C =< $9 ->
-    number_integer_part_rest(Bin, N * 10 + C - $0, Sign, Nexts, Buf);
-number_integer_part_rest(<<Bin/binary>>, N, Sign, Nexts, Buf) ->
-    number_fraction_part(Bin, Sign, N, Nexts, Buf).
+-spec number_integer_part_rest(binary(), non_neg_integer(), 1|-1, [next()], binary(), opt()) -> decode_result().
+number_integer_part_rest(<<C, Bin/binary>>, N, Sign, Nexts, Buf, Opt) when $0 =< C, C =< $9 ->
+    number_integer_part_rest(Bin, N * 10 + C - $0, Sign, Nexts, Buf, Opt);
+number_integer_part_rest(<<Bin/binary>>, N, Sign, Nexts, Buf, Opt) ->
+    number_fraction_part(Bin, Sign, N, Nexts, Buf, Opt).
 
--spec number_fraction_part(binary(), 1|-1, non_neg_integer(), [next()], binary()) -> decode_result().
-number_fraction_part(<<$., Bin/binary>>, Sign, Int, Nexts, Buf) ->
-    number_fraction_part_rest(Bin, Sign, Int, 0, Nexts, Buf);
-number_fraction_part(<<Bin/binary>>, Sign, Int, Nexts, Buf) ->
-    number_exponation_part(Bin, Sign * Int, 0, Nexts, Buf).
+-spec number_fraction_part(binary(), 1|-1, non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+number_fraction_part(<<$., Bin/binary>>, Sign, Int, Nexts, Buf, Opt) ->
+    number_fraction_part_rest(Bin, Sign, Int, 0, Nexts, Buf, Opt);
+number_fraction_part(<<Bin/binary>>, Sign, Int, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, Sign * Int, 0, Nexts, Buf, Opt).
 
--spec number_fraction_part_rest(binary(), 1|-1, non_neg_integer(), non_neg_integer(), [next()], binary()) -> decode_result().
-number_fraction_part_rest(<<C, Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf) when $0 =< C, C =< $9 ->
-    number_fraction_part_rest(Bin, Sign, N * 10 + C - $0, DecimalOffset + 1, Nexts, Buf);
-number_fraction_part_rest(<<Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf) when DecimalOffset > 0 ->
-    number_exponation_part(Bin, Sign * N, DecimalOffset, Nexts, Buf);
-number_fraction_part_rest(Bin, Sign, N, DecimalOffset, Nexts, Buf) ->
-    ?ERROR(number_fraction_part_rest, [Bin, Sign, N, DecimalOffset, Nexts, Buf]).
+-spec number_fraction_part_rest(binary(), 1|-1, non_neg_integer(), non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+number_fraction_part_rest(<<C, Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf, Opt) when $0 =< C, C =< $9 ->
+    number_fraction_part_rest(Bin, Sign, N * 10 + C - $0, DecimalOffset + 1, Nexts, Buf, Opt);
+number_fraction_part_rest(<<Bin/binary>>, Sign, N, DecimalOffset, Nexts, Buf, Opt) when DecimalOffset > 0 ->
+    number_exponation_part(Bin, Sign * N, DecimalOffset, Nexts, Buf, Opt);
+number_fraction_part_rest(Bin, Sign, N, DecimalOffset, Nexts, Buf, Opt) ->
+    ?ERROR(number_fraction_part_rest, [Bin, Sign, N, DecimalOffset, Nexts, Buf, Opt]).
 
--spec number_exponation_part(binary(), integer(), non_neg_integer(), [next()], binary()) -> decode_result().
-number_exponation_part(<<$e, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf);
-number_exponation_part(<<$E, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf);
-number_exponation_part(<<$e, $-, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, -1, 0, true, Nexts, Buf);
-number_exponation_part(<<$E, $-, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, -1, 0, true, Nexts, Buf);
-number_exponation_part(<<$e, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf);
-number_exponation_part(<<$E, Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
-    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf);
-number_exponation_part(<<Bin/binary>>, N, DecimalOffset, Nexts, Buf) ->
+-spec number_exponation_part(binary(), integer(), non_neg_integer(), [next()], binary(), opt()) -> decode_result().
+number_exponation_part(<<$e, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<$E, $+, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<$e, $-, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, -1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<$E, $-, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, -1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<$e, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<$E, Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
+    number_exponation_part(Bin, N, DecimalOffset, 1, 0, true, Nexts, Buf, Opt);
+number_exponation_part(<<Bin/binary>>, N, DecimalOffset, Nexts, Buf, Opt) ->
     case DecimalOffset of
-        0 -> next(Bin, N, Nexts, Buf);
-        _ -> next(Bin, N / math:pow(10, DecimalOffset), Nexts, Buf)
+        0 -> next(Bin, N, Nexts, Buf, Opt);
+        _ -> next(Bin, N / math:pow(10, DecimalOffset), Nexts, Buf, Opt)
     end.
 
--spec number_exponation_part(binary(), integer(), non_neg_integer(), 1|-1, non_neg_integer(), boolean(), [next()], binary()) -> decode_result().
-number_exponation_part(<<C, Bin/binary>>, N, DecimalOffset, ExpSign, Exp, _, Nexts, Buf) when $0 =< C, C =< $9 ->
-    number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp * 10 + C - $0, false, Nexts, Buf);
-number_exponation_part(<<Bin/binary>>, N, DecimalOffset, ExpSign, Exp, false, Nexts, Buf) ->
+-spec number_exponation_part(binary(), integer(), non_neg_integer(), 1|-1, non_neg_integer(), boolean(), [next()], binary(), opt()) -> decode_result().
+number_exponation_part(<<C, Bin/binary>>, N, DecimalOffset, ExpSign, Exp, _, Nexts, Buf, Opt) when $0 =< C, C =< $9 ->
+    number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp * 10 + C - $0, false, Nexts, Buf, Opt);
+number_exponation_part(<<Bin/binary>>, N, DecimalOffset, ExpSign, Exp, false, Nexts, Buf, Opt) ->
     Pos = ExpSign * Exp - DecimalOffset,
-    next(Bin, N * math:pow(10, Pos), Nexts, Buf);
-number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf) ->
-    ?ERROR(number_exponation_part, [Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf]).
+    next(Bin, N * math:pow(10, Pos), Nexts, Buf, Opt);
+number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf, Opt) ->
+    ?ERROR(number_exponation_part, [Bin, N, DecimalOffset, ExpSign, Exp, IsFirst, Nexts, Buf, Opt]).
+
+-spec make_object(jsone:json_object_members(), opt()) -> jsone:json_object().
+make_object(Members, ?OPT{object_format = tuple}) -> {Members};
+make_object([],      _)                           -> [{}];
+make_object(Members, _)                           -> Members.
+
+-spec parse_options([jsone:decode_option()]) -> opt().
+parse_options(Options) ->
+    parse_option(Options, ?OPT{}).
+
+-spec parse_option([jsone:decode_option()], opt()) -> opt().
+parse_option([], Opt) -> Opt;
+parse_option([{object_format,F}|T], Opt) when F =:= tuple; F =:= proplist ->
+    parse_option(T, Opt?OPT{object_format=F}).

+ 34 - 31
src/jsone_encode.erl

@@ -2,7 +2,7 @@
 %%% @private
 %%% @end
 %%%
-%%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
 %%%
 %%% The MIT License
 %%%
@@ -33,12 +33,13 @@
 -export([encode/1, encode/2]).
 
 %%--------------------------------------------------------------------------------
-%% Macros & Types
+%% Macros & Records & Types
 %%--------------------------------------------------------------------------------
 -define(ERROR(Function, Args), {error, {badarg, [{?MODULE, Function, Args, [{line, ?LINE}]}]}}).
 -define(IS_REDUNDANT_UTF8(B1, B2, FirstBitN), (B1 =:= 0 andalso B2 < (1 bsl (FirstBitN + 1)))).
 -define(HEX(N, I), (binary:at(<<"0123456789abcdef">>, (N bsr (I * 4)) band 2#1111))).
 -define(UNICODE_TO_HEX(Code), ?HEX(Code, 3), ?HEX(Code, 2), ?HEX(Code, 1), ?HEX(Code, 0)).
+-define(IS_STR(X), is_binary(X); is_atom(X)).
 
 -type encode_result() :: {ok, binary()} | {error, {Reason::term(), [erlang:stack_item()]}}.
 -type next() :: {array_values, [jsone:json_value()]}
@@ -46,16 +47,15 @@
               | {object_members, jsone:json_object_members()}.
 
 -record(encode_opt_v1, { native_utf8 = false :: boolean() }).
--define(ENCODE_OPT, #encode_opt_v1).
--type encode_opt() :: #encode_opt_v1{}.
+-define(OPT, #encode_opt_v1).
+-type opt() :: #encode_opt_v1{}.
 
 %%--------------------------------------------------------------------------------
 %% Exported Functions
 %%--------------------------------------------------------------------------------
-%% @doc Encodes an erlang term into json text (a utf8 encoded binary)
 -spec encode(jsone:json_value()) -> encode_result().
 encode(Value) ->
-    jsone:try_encode(Value).
+    encode(Value, []).
 
 -spec encode(jsone:json_value(), [jsone:encode_option()]) -> encode_result().
 encode(Value, Options) ->
@@ -65,7 +65,7 @@ encode(Value, Options) ->
 %%--------------------------------------------------------------------------------
 %% Internal Functions
 %%--------------------------------------------------------------------------------
--spec next([next()], binary(), encode_opt()) -> encode_result().
+-spec next([next()], binary(), opt()) -> encode_result().
 next([], Buf, _)             -> {ok, Buf};
 next([Next | Nexts], Buf, Opt) ->
     case Next of
@@ -83,22 +83,26 @@ next([Next | Nexts], Buf, Opt) ->
             end
     end.
 
--spec value(jsone:json_value(), [next()], binary(), encode_opt()) -> encode_result().
+-spec value(jsone:json_value(), [next()], binary(), opt()) -> encode_result().
 value(null, Nexts, Buf, Opt)                         -> next(Nexts, <<Buf/binary, "null">>, Opt);
 value(false, Nexts, Buf, Opt)                        -> next(Nexts, <<Buf/binary, "false">>, Opt);
 value(true, Nexts, Buf, Opt)                         -> next(Nexts, <<Buf/binary, "true">>, Opt);
 value(Value, Nexts, Buf, Opt) when is_integer(Value) -> next(Nexts, <<Buf/binary, (integer_to_binary(Value))/binary>>, Opt);
 value(Value, Nexts, Buf, Opt) when is_float(Value)   -> next(Nexts, <<Buf/binary, (float_to_binary(Value))/binary>>, Opt);
-value(Value, Nexts, Buf, Opt) when is_binary(Value)  -> string(Value, Nexts, Buf, Opt);
+value(Value, Nexts, Buf, Opt) when ?IS_STR(Value)    -> string(Value, Nexts, Buf, Opt);
+value({Value}, Nexts, Buf, Opt)                      -> object(Value, Nexts, Buf, Opt);
+value([{}], Nexts, Buf, Opt)                         -> object([], Nexts, Buf, Opt);
+value([{_, _}|_] = Value, Nexts, Buf, Opt)           -> object(Value, Nexts, Buf, Opt);
 value(Value, Nexts, Buf, Opt) when is_list(Value)    -> array(Value, Nexts, Buf, Opt);
-value({_} = Value, Nexts, Buf, Opt)                  -> object(Value, Nexts, Buf, Opt);
-value(Value, Nexts, Buf, _)                        -> ?ERROR(value, [Value, Nexts, Buf]).
+value(Value, Nexts, Buf, _)                          -> ?ERROR(value, [Value, Nexts, Buf]).
 
--spec string(jsone:json_string(), [next()], binary(), encode_opt()) -> encode_result().
+-spec string(jsone:json_string(), [next()], binary(), opt()) -> encode_result().
 string(<<Str/binary>>, Nexts, Buf, Opt) ->
-    escape_string(Str, Nexts, <<Buf/binary, $">>, Opt).
+    escape_string(Str, Nexts, <<Buf/binary, $">>, Opt);
+string(Str, Nexts, Buf, Opt) ->
+    string(atom_to_binary(Str, utf8), Nexts, Buf, Opt).
 
--spec escape_string(binary(), [next()], binary(), encode_opt()) -> encode_result().
+-spec escape_string(binary(), [next()], binary(), opt()) -> encode_result().
 escape_string(<<"">>,                   Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, $">>, Opt);
 escape_string(<<$", Str/binary>>,       Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $">>, Opt);
 escape_string(<<$\/, Str/binary>>,      Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $\/>>, Opt);
@@ -110,7 +114,7 @@ escape_string(<<$\r, Str/binary>>,      Nexts, Buf, Opt) -> escape_string(Str, N
 escape_string(<<$\t, Str/binary>>,      Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, $\\, $t>>, Opt);
 escape_string(<<0:1, C:7, Str/binary>>, Nexts, Buf, Opt) -> escape_string(Str, Nexts, <<Buf/binary, C>>, Opt);
 escape_string(<<2#110:3, B1:5, 2#10:2, B2:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 5) ->
-    case Opt?ENCODE_OPT.native_utf8 of
+    case Opt?OPT.native_utf8 of
         false ->
             Unicode = (B1 bsl 6) + B2,
             escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
@@ -118,7 +122,7 @@ escape_string(<<2#110:3, B1:5, 2#10:2, B2:6, Str/binary>>, Nexts, Buf, Opt) when
             unicode_char(Str, <<2#110:3, B1:5, 2#10:2, B2:6>>, Nexts, Buf, Opt)
     end;
 escape_string(<<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 4) ->
-    case Opt?ENCODE_OPT.native_utf8 of
+    case Opt?OPT.native_utf8 of
         false ->
             Unicode = (B1 bsl 12) + (B2 bsl 6) + B3,
             escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
@@ -126,7 +130,7 @@ escape_string(<<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6, Str/binary>>, Nexts,
             unicode_char(Str, <<2#1110:4, B1:4, 2#10:2, B2:6, 2#10:2, B3:6>>, Nexts, Buf, Opt)
     end;
 escape_string(<<2#11110:5, B1:3, 2#10:2, B2:6, 2#10:2, B3:6, 2#10:2, B4:6, Str/binary>>, Nexts, Buf, Opt) when not ?IS_REDUNDANT_UTF8(B1, B2, 3) ->
-    case Opt?ENCODE_OPT.native_utf8 of
+    case Opt?OPT.native_utf8 of
         false ->
             Unicode = (B1 bsl 18) + (B2 bsl 12) + (B3 bsl 6) + B4,
             escape_unicode_char(Str, Unicode, Nexts, Buf, Opt);
@@ -139,7 +143,7 @@ escape_string(Str, Nexts, Buf, _) ->
 unicode_char(Str, Char, Nexts, Buf, Opt) ->
     escape_string(Str, Nexts, <<Buf/binary, Char/binary>>, Opt).
 
--spec escape_unicode_char(binary(), char(), [next()], binary(), encode_opt()) -> encode_result().
+-spec escape_unicode_char(binary(), char(), [next()], binary(), opt()) -> encode_result().
 escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf, Opt) when Unicode =< 16#FFFF ->
     escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(Unicode)>>, Opt);
 escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf, Opt) ->
@@ -147,32 +151,31 @@ escape_unicode_char(<<Str/binary>>, Unicode, Nexts, Buf, Opt) ->
     <<High:10, Low:10>> = <<(Unicode - 16#10000):20>>, % XXX: inefficient
     escape_string(Str, Nexts, <<Buf/binary, $\\, $u, ?UNICODE_TO_HEX(High + 16#D800), $\\, $u, ?UNICODE_TO_HEX(Low + 16#DC00)>>, Opt).
 
--spec array(jsone:json_array(), [next()], binary(), encode_opt()) -> encode_result().
+-spec array(jsone:json_array(), [next()], binary(), opt()) -> encode_result().
 array(List, Nexts, Buf, Opt) ->
     array_values(List, Nexts, <<Buf/binary, $[>>, Opt).
 
--spec array_values(jsone:json_array(), [next()], binary(), encode_opt()) -> encode_result().
+-spec array_values(jsone:json_array(), [next()], binary(), opt()) -> encode_result().
 array_values([],       Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, $]>>, Opt);
 array_values([X | Xs], Nexts, Buf, Opt) -> value(X, [{array_values, Xs} | Nexts], Buf, Opt).
 
--spec object(jsone:json_object(), [next()], binary(), encode_opt()) -> encode_result().
-object({Members}, Nexts, Buf, Opt) ->
+-spec object(jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
+object(Members, Nexts, Buf, Opt) ->
     object_members(Members, Nexts, <<Buf/binary, ${>>, Opt).
 
--spec object_members(jsone:json_object_members(), [next()], binary(), encode_opt()) -> encode_result().
-object_members([],                             Nexts, Buf, Opt) -> next(Nexts, <<Buf/binary, $}>>, Opt);
-object_members([{<<Key/binary>>, Value} | Xs], Nexts, Buf, Opt) -> string(Key, [{object_value, Value, Xs} | Nexts], Buf, Opt);
-object_members(Arg, Nexts, Buf, _)                            -> ?ERROR(object_members, [Arg, Nexts, Buf]).
+-spec object_members(jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
+object_members([],                             Nexts, Buf, Opt)        -> next(Nexts, <<Buf/binary, $}>>, Opt);
+object_members([{Key, Value} | Xs], Nexts, Buf, Opt) when ?IS_STR(Key) -> string(Key, [{object_value, Value, Xs} | Nexts], Buf, Opt);
+object_members(Arg, Nexts, Buf, _)                                     -> ?ERROR(object_members, [Arg, Nexts, Buf]).
 
--spec object_value(jsone:json_value(), jsone:json_object_members(), [next()], binary(), encode_opt()) -> encode_result().
+-spec object_value(jsone:json_value(), jsone:json_object_members(), [next()], binary(), opt()) -> encode_result().
 object_value(Value, Members, Nexts, Buf, Opt) ->
     value(Value, [{object_members, Members} | Nexts], <<Buf/binary, $:>>, Opt).
 
-
--spec parse_options([jsone:encode_option()]) -> encode_opt().
+-spec parse_options([jsone:encode_option()]) -> opt().
 parse_options(Options) ->
-    parse_option(Options, ?ENCODE_OPT{}).
+    parse_option(Options, ?OPT{}).
 
 parse_option([], Opt) -> Opt;
 parse_option([native_utf8|T], Opt) ->
-    parse_option(T, Opt?ENCODE_OPT{native_utf8=true}).
+    parse_option(T, Opt?OPT{native_utf8=true}).

+ 9 - 7
test/jsone_decode_tests.erl

@@ -1,4 +1,4 @@
-%% Copyright (c) 2013-2014, Takeru Ohta <phjgt308@gmail.com>
+%% Copyright (c) 2013-2015, Takeru Ohta <phjgt308@gmail.com>
 %% coding: latin-1
 -module(jsone_decode_tests).
 
@@ -75,7 +75,7 @@ decode_test_() ->
               ?assertMatch({error, {badarg, _}}, jsone_decode:decode(<<"0.1e--1">>)), % duplicated sign
               ?assertEqual({ok, 0.1, <<".2">>}, jsone_decode:decode(<<"0.1.2">>))     % duplicated '.': interpreted as individual tokens
       end},
-     
+
      %% Strings
      {"simple string",
       fun () ->
@@ -162,19 +162,21 @@ decode_test_() ->
      {"simple object",
       fun () ->
               Input    = <<"{\"1\":2,\"key\":\"value\"}">>,
-              Expected = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
-              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
+              Expected = {[{<<"1">>, 2},{<<"key">>, <<"value">>}]},
+              ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input)),
+              ?assertEqual({ok, element(1, Expected), <<"">>}, jsone_decode:decode(Input, [{object_format, proplist}]))
       end},
      {"object: contains whitespaces",
       fun () ->
               Input    = <<"{  \"1\" :\t 2,\n\r\"key\" :   \n  \"value\"}">>,
-              Expected = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
+              Expected = {[{<<"1">>, 2},{<<"key">>, <<"value">>}]},
               ?assertEqual({ok, Expected, <<"">>}, jsone_decode:decode(Input))
       end},
      {"empty object",
       fun () ->
               ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{}">>)),
-              ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>))
+              ?assertEqual({ok, {[]}, <<"">>}, jsone_decode:decode(<<"{ \t\r\n}">>)),
+              ?assertEqual({ok, [{}], <<"">>}, jsone_decode:decode(<<"{}">>, [{object_format, proplist}]))
       end},
      {"object: trailing comma is disallowed",
       fun () ->
@@ -212,7 +214,7 @@ decode_test_() ->
      {"compound data",
       fun () ->
               Input    = <<"  [true, {\"1\" : 2, \"array\":[[[[1]]], {\"ab\":\"cd\"}, false]}, null]   ">>,
-              Expected = [true, {[{<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}, {<<"1">>, 2}]}, null],
+              Expected = [true, {[{<<"1">>, 2}, {<<"array">>, [[[[1]]], {[{<<"ab">>, <<"cd">>}]}, false]}]}, null],
               ?assertEqual({ok, Expected, <<"   ">>}, jsone_decode:decode(Input))
       end}
     ].

+ 18 - 6
test/jsone_encode_tests.erl

@@ -52,6 +52,10 @@ encode_test_() ->
       fun () ->
               ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(<<"abc">>))
       end},
+     {"atom is regarded as string",
+      fun () ->
+              ?assertEqual({ok, <<"\"abc\"">>}, jsone_encode:encode(abc))
+      end},
      {"string: contains escaped characters",
       fun () ->
               Input    = <<"\"\/\\\b\f\n\r\t">>,
@@ -98,21 +102,29 @@ encode_test_() ->
      %% Objects
      {"simple object",
       fun () ->
-              Input    = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
+              Input1   = {[{<<"key">>, <<"value">>}, {<<"1">>, 2}]},
+              Input2   = [{<<"key">>, <<"value">>}, {<<"1">>, 2}],
               Expected = <<"{\"key\":\"value\",\"1\":2}">>,
-              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
       end},
      {"empty object",
       fun () ->
-              Input    = {[]},
+              Input1   = {[]},
+              Input2   = [{}],
               Expected = <<"{}">>,
-              ?assertEqual({ok, Expected}, jsone_encode:encode(Input))
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input1)),
+              ?assertEqual({ok, Expected}, jsone_encode:encode(Input2))
+      end},
+     {"atom key is allowed",
+      fun () ->
+              Expected = <<"{\"key\":2}">>,
+              ?assertEqual({ok, Expected}, jsone_encode:encode({[{key, 2}]}))
       end},
      {"non binary object member key is disallowed",
       fun () ->
               ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{1, 2}]})),
-              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{"1", 2}]})),
-              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{true, 2}]}))
+              ?assertMatch({error, {badarg, _}}, jsone_encode:encode({[{"1", 2}]}))
       end},
 
      %% Others