Module jsone
February 9, 2022 · View on GitHub
JSON decoding/encoding module.
Data Types
common_option()
common_option() = undefined_as_null
undefined_as_null:
- Treats
undefinedin Erlang as the conversion target fornullin JSON. This means thatundefinedwill be encoded tonullandnullwill be decoded toundefined
datetime_encode_format()
datetime_encode_format() = datetime_format() | {Format::datetime_format(), TimeZone::timezone()}
Datetime encoding format.
The default value of TimeZone is utc.
%
% Universal Time
%
> jsone:encode({{2000, 3, 10}, {10, 3, 58}}, [{datetime_format, iso8601}]).
<<"\"2000-03-10T10:03:58Z\"">>
%
% Local Time (JST)
%
> jsone:encode({{2000, 3, 10}, {10, 3, 58}}, [{datetime_format, {iso8601, local}}]).
<<"\"2000-03-10T10:03:58+09:00\"">>
%
% Explicit TimeZone Offset
%
> jsone:encode({{2000, 3, 10}, {10, 3, 58}}, [{datetime_format, {iso8601, -2*60*60}}]).
<<"\"2000-03-10T10:03:58-02:00\"">>
datetime_format()
datetime_format() = iso8601
decode_option()
decode_option() = {object_format, tuple | proplist | map} | {allow_ctrl_chars, boolean()} | reject_invalid_utf8 | {keys, binary | atom | existing_atom | attempt_atom} | {duplicate_map_keys, first | last} | common_option()
object_format:
- Decoded JSON object format
tuple: An object is decoded as{[]}if it is empty, otherwise{[{Key, Value}]}.proplist: An object is decoded as[{}]if it is empty, otherwise[{Key, Value}].map: An object is decoded as#{}if it is empty, otherwise#{Key => Value}.- default:
mapif OTP version is OTP-17 or more,tupleotherwise
allow_ctrl_chars:
- If the value is
true, strings which contain unescaped control characters will be regarded as a legal JSON string - default:
false
reject_invalid_utf8:
- Rejects JSON strings which contain invalid UTF-8 byte sequences
keys:
Defines way how object keys are decoded. The default value is binary.
The option is compatible with labels option in jsx.
binary: The key is left as a string which is encoded as binary. It's default and backward compatible behaviour.atom: The key is converted to an atom. Results inbadargif Key value regarded as UTF-8 is not a valid atom.existing_atom: Returns existing atom. Any key value which is not existing atom raisesbadargexception.attempt_atom: Returns existing atom asexisting_atombut returns a binary string if fails find one.
duplicate_map_keys:
https://www.ietf.org/rfc/rfc4627.txt says that keys SHOULD be
unique, but they don't have to be. Most JSON parsers will either
give you the value of the first, or last duplicate property
encountered. When object_format is tuple or proplist all
duplicates are returned. When object_format is map by default
the first instance of a duplicate is returned. Setting
duplicate_map_keys to last will change this behaviour to return
the last such instance.
- If the value is
firstthen the first duplicate key/value is returned. - If the value is
lastthen the last duplicate key/value is returned. - default:
first
encode_option()
encode_option() = native_utf8 | native_forward_slash | canonical_form | {float_format, [float_format_option()]} | {datetime_format, datetime_encode_format()} | {object_key_type, string | scalar | value} | {space, non_neg_integer()} | {indent, non_neg_integer()} | {map_unknown_value, fun((term()) -> {ok, json_value()} | error)} | common_option()
native_utf8:
- Encodes non ASCII UTF-8 characters as a human-readable(non-escaped) string
native_forward_slash:
- Prevents forward slashes in a JSON string from being escaped
canonical_form:
- produce a canonical form of a JSON document
{float_format, Options}:
- Encodes a
float()value in the format which specified byOptions - default:
[{scientific, 20}]
{datetime_format, Format}:
- Encodes a
calendar:datetime()value in the format which specified byFormat - default:
{iso8601, utc}
object_key_type:
- Allowable object key type
string: Only string values are allowed (i.e.json_string()type)scalar: In addition tostring, following values are allowed: nulls, booleans, numerics (i.e.json_scalar()type)value: Any json compatible values are allowed (i.e.json_value()type)- default:
string - NOTE: If
scalarorvalueoption is specified, nonjson_string()key will be automatically converted to abinary()value (e.g.1=><<"1">>,#{}=><<"{}">>)
{space, N}:
- Inserts
Nspaces after every comma and colon - default:
0
{indent, N}:
- Inserts a newline and
Nspaces for each level of indentation - default:
0
{map_unknown_value, Fun}:
- If specified, unknown values encountered during an encoding process are converted to
json_value()by applyingFun.
float_format_option()
float_format_option() = {scientific, Decimals::0..249} | {decimals, Decimals::0..253} | compact
scientific:
- The float will be formatted using scientific notation with
Decimalsdigits of precision.
decimals:
- The encoded string will contain at most
Decimalsnumber of digits past the decimal point. - If
compactis provided the trailing zeros at the end of the string are truncated.
For more details, see erlang:float_to_list/2.
> jsone:encode(1.23).
<<"1.22999999999999998224e+00">>
> jsone:encode(1.23, [{float_format, [{scientific, 4}]}]).
<"1.2300e+00">>
> jsone:encode(1.23, [{float_format, [{scientific, 1}]}]).
<<"1.2e+00">>
> jsone:encode(1.23, [{float_format, [{decimals, 4}]}]).
<<"1.2300">>
> jsone:encode(1.23, [{float_format, [{decimals, 4}, compact]}]).
<<"1.23">>
json_array()
json_array() = [json_value()]
json_boolean()
json_boolean() = boolean()
json_number()
json_number() = number()
json_object()
json_object() = json_object_format_tuple() | json_object_format_proplist() | json_object_format_map()
json_object_format_map()
json_object_format_map() = #{}
json_object_format_proplist()
json_object_format_proplist() = [{}] | json_object_members()
json_object_format_tuple()
json_object_format_tuple() = {json_object_members()}
json_object_members()
json_object_members() = [{json_string(), json_value()}]
json_scalar()
json_scalar() = json_boolean() | json_number() | json_string()
json_string()
json_string() = binary() | atom() | calendar:datetime()
NOTE: decode/1 always returns binary() value
json_term()
json_term() = {{json, iolist()}} | {{json_utf8, unicode:chardata()}}
json_term() allows inline already encoded JSON value. json variant
expects byte encoded utf8 data values as list members. json_utf8 expect
Unicode code points as list members. Binaries are copied "as is" in both
variants except json_utf8 will check if binary contain valid UTF-8
encoded data. In short, json uses erlang:iolist_to_binary/1 and
json_utf8 uses unicode:chardata_to_binary/1 for encoding.
A simple example is worth a thousand words.
1> S = "hélo".
"hélo"
2> shell:strings(false).
true
3> S.
[104,233,108,111]
4> B = jsone:encode({{json, S}}). % invalid UTF-8
<<104,233,108,111>>
5> B2 = jsone:encode({{json_utf8, S}}). % valid UTF-8
<<104,195,169,108,111>>
6> jsone:encode({{json, B}}).
<<104,233,108,111>>
7> jsone:encode({{json_utf8, B}}).
** exception error: {invalid_json_utf8,<<104>>,<<233,108,111>>}
in function jsone_encode:value/4
called as jsone_encode:value({json_utf8,<<104,233,108,111>>},
[],<<>>,
{encode_opt_v2,false,
[{scientific,20}],
{iso8601,0},
string,0,0})
in call from jsone:encode/2 (/home/hynek/work/altworx/jsone/_build/default/lib/jsone/src/jsone.erl, line 302)
8> jsone:encode({{json_utf8, B2}}).
<<104,195,169,108,111>>
9> shell:strings(true).
false
10> jsone:encode({{json_utf8, B2}}).
<<"hélo"/utf8>>
11> jsone:encode({{json, binary_to_list(B2)}}). % UTF-8 encoded list leads to valid UTF-8
<<"hélo"/utf8>>
json_value()
json_value() = json_number() | json_string() | json_array() | json_object() | json_boolean() | null | undefined | json_term()
stack_item()
stack_item() = {Module::module(), Function::atom(), Arity::arity() | (Args::[term()]), Location::[{file, Filename::string()} | {line, Line::pos_integer()}]}
An item in a stack back-trace.
Note that the erlang module already defines the same stack_item/0 type,
but it is not exported from the module.
So, maybe as a temporary measure, we redefine this type for passing full dialyzer analysis.
timezone()
timezone() = utc | local | utc_offset_seconds()
utc_offset_seconds()
utc_offset_seconds() = -86399..86399
Function Index
| decode/1 | Equivalent to decode(Json, []). |
| decode/2 | Decodes an erlang term from json text (a utf8 encoded binary). |
| encode/1 | Equivalent to encode(JsonValue, []). |
| encode/2 | Encodes an erlang term into json text (a utf8 encoded binary). |
| try_decode/1 | Equivalent to try_decode(Json, []). |
| try_decode/2 | Decodes an erlang term from json text (a utf8 encoded binary). |
| try_encode/1 | Equivalent to try_encode(JsonValue, []). |
| try_encode/2 | Encodes an erlang term into json text (a utf8 encoded binary). |
Function Details
decode/1
decode(Json::binary()) -> json_value()
Equivalent to decode(Json, []).
decode/2
decode(Json::binary(), Options::[decode_option()]) -> json_value()
Decodes an erlang term from json text (a utf8 encoded binary)
Raises an error exception if input is not valid json
> jsone:decode(<<"1">>, []).
1
> 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)
encode/1
encode(JsonValue::json_value()) -> binary()
Equivalent to encode(JsonValue, []).
encode/2
encode(JsonValue::json_value(), Options::[encode_option()]) -> binary()
Encodes an erlang term into json text (a utf8 encoded binary)
Raises an error exception if input is not an instance of type json_value()
> jsone:encode([1, null, 2]).
<<"[1,null,2]">>
> jsone:encode([1, self(), 2]). % A pid is not a json value
** exception error: bad argument
in function jsone_encode:value/3
called as jsone_encode:value(<0,34,0>,[{array_values,[2]}],<<"[1,">>)
in call from jsone:encode/1 (src/jsone.erl, line 97)
try_decode/1
try_decode(Json::binary()) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [stack_item()]}}
Equivalent to try_decode(Json, []).
try_decode/2
try_decode(Json::binary(), Options::[decode_option()]) -> {ok, json_value(), Remainings::binary()} | {error, {Reason::term(), [stack_item()]}}
Decodes an erlang term from json text (a utf8 encoded binary)
> jsone:try_decode(<<"[1,2,3] \"next value\"">>, []).
{ok,[1,2,3],<<" \"next value\"">>}
> jsone:try_decode(<<"wrong json">>, []).
{error,{badarg,[{jsone_decode,number_integer_part,
[<<"wrong json">>,1,[],<<>>],
[{line,208}]}]}}
try_encode/1
try_encode(JsonValue::json_value()) -> {ok, binary()} | {error, {Reason::term(), [stack_item()]}}
Equivalent to try_encode(JsonValue, []).
try_encode/2
try_encode(JsonValue::json_value(), Options::[encode_option()]) -> {ok, binary()} | {error, {Reason::term(), [stack_item()]}}
Encodes an erlang term into json text (a utf8 encoded binary)
> jsone:try_encode([1, null, 2]).
{ok,<<"[1,null,2]">>}
> jsone:try_encode([1, hoge, 2]). % 'hoge' atom is not a json value
{error,{badarg,[{jsone_encode,value,
[hoge,[{array_values,[2]}],<<"[1,">>],
[{line,86}]}]}}