Home - crownedgrouse/argos GitHub Wiki

Welcome to the argos wiki!

Preamble

On contrary to its deprecated predecessor jason (in addition to be confusing with Elixir eponym library), argos do not implement any JSON parser and use the json library supplied by Erlang/OTP since 27.0.

  • argos's bias is to be able to encode and decode records at runtime, without any code manipulation at compile time. This make argos easy to use, with a minimal footprint in your own source code.
  • argos 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.
  • argos allow you to use JSON to records translation when source is not stable and JSON object can change without any warning. This is particularly 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 argos gives you, among others.
  • Only very few options to be user friendly.
  • argos allow to do GraphQL queries

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

Mode

Mode is driving argos general behavior for decoding.

Mode is an atom among otp, struct, map, proplist, record and graphql to be passed in tuple {mode, ...} in options.

  • otp : Special and default value if no mode set. argos will behave like json OTP library (maps and keys and values as binaries). binary option will be ignored with such mode.
  • struct : Decode JSON as tuples of key/value (binary data).
  • map : Decode JSON as maps.
  • proplist : Decode JSON as proplist.
  • record : Decode JSON as records.
  • graphql : Decode JSON as records but for a GraphQL usage.

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.

:warning: 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

argos 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 argos 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 :

argos: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.

argos 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.

argos 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).