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 makeargoseasy to use, with a minimal footprint in your own source code.argoslet you easily convert JSON object to record specification, and create for you ad-hoc modules to handle records. Even with deeply nested JSON objects.argosallow 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
argosgives you, among others. - Only very few options to be user friendly.
argosallow 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.argoswill behave likejsonOTP library (maps and keys and values as binaries).binaryoption 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 :
- By passing record's key list as option :
{records, [{RecordName1, [Field1, ...]}, ...]} - By asking
argosto 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).