Skip to content
Eric Pailleau edited this page Jul 15, 2023 · 23 revisions

Welcome to the jason wiki!

Preamble

Yet another Nth JSON Erlang project, while there are already good ones ?

Yes, jason offer features mainly for Erlang records handling, which are not widely proposed in other projects.

jason do not use NIF, so if performance is your main interest, see other well known projects using NIFs. However see Benchmark.

So what makes jason usefull for me, then ?

  • jason's bias is to be able to encode and decode records at runtime, without any code manipulation at compile time. This make jason easy to use, with a minimal footprint in your own source code.
  • jason let you easily convert JSON object to record specification, and create for you ad-hoc modules to handle records. Even with deeply nested JSON objects.
  • jason allow you to use JSON to records translation when source is not stable and JSON object can change without any warning. This is particulary interesting when JSON source is coming from third party.
  • Float are converted with automatic precision without need to set a precision depth.
  • Pretty printing JSON in several indentation format is another rare feature that jason gives you, among others.
  • Only very few options to be user friendly.

Some important things are to be known before using jason. Please read carefully below chapters.

Keys and values handling

Keys

Keys, if no mode is set, or if {mode, struct} is used, are utf8 binaries. For all other modes, unless option {binary, k} or {binary, kv} is set, keys will be converted to atoms.

However, if key length is more than 255 characters, or containing utf8 characters and local Erlang release < 20.0, key will be converted to binary. This will not be possible for {mode, record} for which key must be atoms, and will raise an error in such case. This case should be rare fortunately.

Values

Values, if no mode is set, or if {mode, struct} is used are utf8 binaries. For all other modes, unless option {binary, v} or {binary, kv} is set, values will be converted to string if printable, otherwise kept in utf8 binary.

⚠️ It is strongly discouraged to use {binary, v} or {binary, kv} with {mode, record} as it can result in badarg errors, especially with nested records where v part is a record and not convertible to binary.

Records

jason was created initially to handle easily Erlang records from and to JSON.

Encoding

Erlang records are, finally, only tuples with an atom name as first element. This first element is lost while encoded in JSON, as JSON object are 'anonymous' (except by declaring object name in object itself). The problem is to affect the keys (left side) of JSON entries.

There is two ways to do this :

  1. By passing record's key list as option : {records, [{RecordName1, [Field1, ...]}, ...]}
  2. By asking jason to extract record info from module(s) abstract code : {records, [ModName, ...]}

First way is easy by using built-in record_info(fields, RecordName). First way is recommended when possible for obvious performance reason.

Second way is slower, but is handy when many record definitions has to be passed to encoding. This make the code also easy for records declared locally :

jason:encode(Something, [{records, [?MODULE]}]).

Make sure however that abstract code will be available after compilation. Compile native is not the case...

Decoding

Counterpart of the fact that JSON objects are 'anonymous', i.e without name declaration, is that we cannot give a meaningful name (first element of tuple) to generated Erlang records, without magic.

jason decode JSON object by creating dynamically named records based on a portable hash (phash2) on keys and value type.

For instance {"k1": 1,"k2": "ab"} creates a record {'8056669',1,"ab"} (i.e '8056669'#{'k1'=1, 'k2'="ab"}). Record name is phash2([{k1, integer},{k2,list}]) casted to atom.

Note : hash collision is possible in theory but very unlikely in real life usage.

jason create for you dynamically compiled module, called with same name than record, to handle easily those records.

Deeply nested JSON object are cleanly handled bottom up, so that each parent record use child record types.

Note : using option {binary, v} or {binary, kv} will create a module with a different hash than without option, and also a different record definition (value is expected to be binary and not list).

Clone this wiki locally