Module jsone

JSON decoding/encoding module.

Description

JSON decoding/encoding module

Data Types

common_option()

common_option() = undefined_as_null

undefined_as_null:
- Treats undefined in Erlang as the conversion target for null in JSON. This means that undefined will be encoded to null and null will be decoded to undefined

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: map if OTP version is OTP-17 or more, tuple otherwise

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 in badarg if Key value regarded as UTF-8 is not a valid atom.
- existing_atom: Returns existing atom. Any key value which is not existing atom raises badarg exception.
- attempt_atom: Returns existing atom as existing_atom but 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 first then the first duplicate key/value is returned.
- If the value is last then 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, undefined | fun((term()) -> {ok, json_value()} | error)} | skip_undefined | 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 by `Options
- default: [{scientific, 20}]

{datetime_format, Format}`: - Encodes a `calendar:datetime()` value in the format which specified by `Format
- default: {iso8601, utc}

object_key_type: - Allowable object key type
- string: Only string values are allowed (i.e. json_string() type)
- scalar: In addition to string, 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 scalar or value option is specified, non json_string() key will be automatically converted to a binary() value (e.g. 1 => <<"1">>, #{} => <<"{}">>)

{space, N}:
- Inserts N spaces after every commna and colon
- default: 0

{indent, N}:
- Inserts a newline and N spaces for each level of indentation
- default: 0

skip_undefined:
- If speficied, each entry having undefined value in a object isn't included in the result JSON

{map_unknown_value, Fun}`: <br /> - If `Fun is a function, unknown values encountered during an encoding process are converted to json_value()` by applying `Fun.
- If Fun is undefined, the encoding results in an error if there are unknown values.
- default: term_to_json_string/1

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 Decimals digits of precision.

decimals:
- The encoded string will contain at most Decimals number of digits past the decimal point.
- If compact is 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() = 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/1Equivalent to decode(Json, []).
decode/2Decodes an erlang term from json text (a utf8 encoded binary).
encode/1Equivalent to encode(JsonValue, []).
encode/2Encodes an erlang term into json text (a utf8 encoded binary).
ip_address_to_json_string/1Convert an IP address into a text representation.
term_to_json_string/1Converts the given term X to its string representation (i.e., the result of io_lib:format("~p", [X])).
try_decode/1Equivalent to try_decode(Json, []).
try_decode/2Decodes an erlang term from json text (a utf8 encoded binary).
try_encode/1Equivalent to try_encode(JsonValue, []).
try_encode/2Encodes 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)

ip_address_to_json_string/1

ip_address_to_json_string(X::inet:ip_address() | any()) -> {ok, json_string()} | error

Convert an IP address into a text representation.

This function can be specified as the value of the map_unknown_value encoding option.

This function formats IPv6 addresses by following the recommendation defined in RFC 5952. Note that the trailing 32 bytes of special IPv6 addresses such as IPv4-Compatible (::X.X.X.X), IPv4-Mapped (::ffff:X.X.X.X), IPv4-Translated (::ffff:0:X.X.X.X) and IPv4/IPv6 translation (64:ff9b::X.X.X.X and 64:ff9b:1::X.X.X.X ~ 64:ff9b:1:ffff:ffff:ffff:X.X.X.X) are formatted using the IPv4 format.

  > EncodeOpt = [{map_unknown_value, fun jsone:ip_address_to_json_string/1}].
 
  > jsone:encode(#{ip => {127, 0, 0, 1}}, EncodeOpt).
  <<"{\"ip\":\"127.0.0.1\"}">>
 
  > {ok, Addr} = inet:parse_address("2001:DB8:0000:0000:0001:0000:0000:0001").
  > jsone:encode(Addr, EncodeOpt).
  <<"\"2001:db8::1:0:0:1\"">>
 
  > jsone:encode([foo, {0, 0, 0, 0, 0, 16#FFFF, 16#7F00, 16#0001}], EncodeOpt).
  <<"[\"foo\",\"::ffff:127.0.0.1\"]">>

term_to_json_string/1

term_to_json_string(X::term()) -> {ok, json_string()} | error

Converts the given term X to its string representation (i.e., the result of io_lib:format("~p", [X])).

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}]}]}}


Generated by EDoc