2600Hz JSON
Manipulating JSON objects in Erlang.
Accessors
Getters
The kz_json module provides a generic accessor, get_value(Path, JObj[, Default]) that will return the value at Path ('undefined' or Default if missing).
There are also a number of accessors that will make sure the value retrieved is of a certain type; otherwise the Default is used. Functions like get_ne_binary_value will make sure the Value is a non-empty binary, for instance.
Another accessor is get_first_defined(Paths, JObj[, Default]) which takes a list of paths to search, returning the first non-'undefined' value found iterating through Paths; otherwise 'undefined' or Default is returned.
get_keys(JObj): returns the top-level keys of theJObj.get_values(JObj): will return a tuple{Values, Keys}from the object (similar tolists:unzip/2).values(JObj): returns just the top-levelValuesof the object.new(): returns an empty JSON object.
Setters
set_value(Path, Value, JObj)will putValueatPath(including creating nested objects as necessary).set_values([{Path, Value}], JObj)will putValuesatPaths(including creating nested objects as necessary).insert_value(Path, Value, JObj)will only putValueatPathif there isn’t a value there already.insert_values([{Path, Value}], JObj)will only putValueatPathif there isn’t a value there already.
Deleting keys
delete_key(Path, JObj[, 'prune' | 'no_prune']): will delete the value at Path from the JObj. You can optionally prune the resulting empty object ('no_prune'is the default).delete_keys(Paths, JObj): will deletePathsusing'no_prune'. Useprune_keys(Paths, JObj)if you want empty objects pruned.
Note that set_value(Path, 'null', JObj) is equivalent to delete_key(Path, JObj).
Predicates
Several predicates exist that will return booleans.
is_true(Path, JObj[, Default]): returns if the value atPathis “true” in some form (atom, list, binary).is_falseexists as well for “false”.is_defined(Path, JObj): Returns if the value atPathis not'undefined'.is_empty(JObj): returnstrueifJObjis the empty JSON object ({[]}currently but could be#{}if the switch to maps is made).is_json_object(JObj),are_json_objects(JObjs): Superficial check for the internal JSON structure. Useis_valid_json_object(JObj)to recurse into the data structure.is_json_term(Term): returns if theTermis aJSON-ableterm. Terms likepid(),reference(), and such have no parallel in JSON and are thus considered to not be JSON terms.are_equal(JObj1, JObj2): compares two objects for equality, irrespective of how the underlying ordering of keys.
Converters
Away from JSON objects
to_proplist(JObj): Returns the top-level object as a proplist instead (nested objects remain objects).to_map(JObj): Returns a map representation of the objectrecursive_to_proplist(JObj): Converts nested objects to proplists as well
To JSON objects
from_list(Proplist): Top-level change from proplist to objectfrom_map(Map): Converts map back to JSON object data structurefrom_list_recursive(Proplist): Equivalent tofrom_list_recursive/2with default option:#{ascii_list_enforced => 'true', invalid_as_null => 'true'}from_list_recursive(Proplist, #{}=Options): Converts nested proplists to objects as well. Options are:ascii_list_enforced: If the value of a proplist item is list and all of the elements are ASCII characters, convert the value to binary. This also converts empty list[]to binary. If you are sure that you don’t have any string value, but expect and empty list as value, set this option tofalseto not convert the empty list to binaryinvalid_as_null: If the value of a proplist item is not any oflist(),binary(),atom(),integer(),float(),kz_time:date(),kz_time:datetime(),kz_json:object()orkz_term:proplist()then set the value tonull, otherwise throw{'error', kz_term:ne_binary()}
Merging Objects
You can merge two (or more) objects together and specify which objects’ value will be used when there are conflicting keys.
merge([JObj1, JObj2,...]): Defaults to using themerge_rightstrategy where the object on the “right” will have its value used when both objects have a value at a given key path.merge(Strategy, JObjs): Choose a strategy, eitherfun kz_json:merge_right/2orfun kz_json:merge_left/2(or define your own), and apply it to the list of objects.
Both merge_left/2 and merge_right/2 will keep the left or right empty objects (respectively) when they exist at a given key path if the other value is also an object (empty or not). To override this behavior and always recursively merge the values when both are objects, specify 'recursive' => 'true' in the Options map passed in merge(JObjs, Options), merge(JObj1, JObj2, Options), merge(Strategy, JObjs, Options), or merge(JObj1, JObj2, Options), as arg 4 to merge/4, or as arg 5 to merge/5.
Handling null
Sometimes it is beneficial to include the null atom as a value (for downstream processing perhaps) versus triggering a delete_key equivalent.
set_value and merge can take an optional map #{'keep_null' => 'true'} to ensure null is kept in the resulting data structure.
List-like operations
JSON objects are iterable and kz_json has equivalents to the lists module for filter/{2,3}, filtermap/2, map/2, fold{l,r}/3, foreach/2, all/2, and any/2 among others.
Another of note is find(Path, JObjs[, Default]) which searchs a list of JSON objects for a key path. There is also a find_first_defind(Paths, JObjs[, Default]) which will find(Path, JObjs), continuing to the next Path if 'undefined' is returned.
There is also a find_value(Path, Value, JObjs[, Default]) which will find the first JObj in the list that has Value at Path.
Lifting common JSON properties
Basic usage
kz_json:lift_common_properties([JObj1, JObj2]) ->
{CommonJObj, [UniqueJObj1, UniqueJObj2]}.
All key/value pairs common to each JObj in the list will be collected into CommonJObj and removed from each JObj.
Blacklists
Sometimes you know there are keys that shouldn’t be lifted (like leg-only variables on endpoints vs channel variables):
kz_json:lift_common_properties([JObj1, JObj2,...], [Key1, Path2]) ->
{CommonJObj, [UniqueJObj1, UniqueJObj2,...]}.
This will not allow Key1 and Path2 to be in CommonJObj and will remain in the objects that have it set. Note that the blacklist can be keys (<<"key">>) or paths ([<<"key">>, <<"subkey">>]).