Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

JS: JSON and type helpers

JSON and type helpers

The C side builds every payload with jansson. The JS side gives the same function names on top of plain objects and arrays, so a gclass reads the same in both languages.

Source code: src/helpers.js


Copy and compare

json_deep_copy(obj)

Gives a deep copy of one value, with structuredClone().

duplicate_objects(obj)

Gives a new object in which every value is a deep copy.

json_is_identical(a, b)

Tells if two JSON values hold the same data. It compares the content, and not the reference.


Update an object

The three update functions are not recursive. They change the destination in place, and they go one level deep.

json_object_update(dst, src)

Adds the new keys of src and writes over the keys that dst holds already.

json_object_update_existing(dst, src)

Writes only the keys that dst holds already. It adds nothing.

json_object_update_missing(dst, src)

Adds only the keys that dst does not hold. It writes over nothing.


Read and write

json_object_get(o, k)

Gives the value of a key.

json_object_set(o, k, v)

Writes a key.

json_object_set_new(o, k, v)

Writes a key. In JavaScript it is the same as json_object_set().

json_object_del(o, k)

Deletes a key.

json_array_append(a, v)

Puts a value at the end of an array.

json_array_append_new(a, v)

Puts a value at the end of an array. In JavaScript it is the same as json_array_append().

json_array_remove(a, idx)

Takes the element at an index out of an array.

json_array_extend(destination, source)

Puts every element of source at the end of destination, and gives destination back. An empty source changes nothing.


Size

json_object_size(a)

Gives the quantity of keys of an object. It gives 0 for every other type.

json_array_size(a)

Gives the length of an array. It gives 0 for every other type.

json_size(a)

Gives the size of any JSON value: the quantity of keys of an object, the length of an array, 1 for a string that is not empty, and 0 for everything else.

empty_json(a)

Tells if there is nothing usable in the value: null or undefined, an empty object or array, an empty string, or any scalar. It is json_size(a) === 0.

This is the twin of empty_json() in the C runtime, and there it is not a convenience: an attribute declared DTP_JSON with a null default holds json_null(), a valid pointer, so if(!jn) is dead code. In JavaScript if(!x) does work, which is why a port between the two runtimes gets this wrong in both directions. Write the same question in both.

The other question, “absent or explicitly null”, is is_null() here. JavaScript has no second null to tell apart.


Type predicates

Each one gives a boolean.

is_object(v)

Tells if the value is an object. An array is not an object here, and null is not an object.

is_array(v)

Tells if the value is an array.

is_string(v)

Tells if the value is a string.

is_number(v)

Tells if the value is a number. NaN and Infinity are not numbers here.

is_boolean(v)

Tells if the value is a boolean.

is_null(v)

Tells if the value is null or undefined.

is_date(v)

Tells if the value is a Date.

is_function(v)

Tells if the value is a function.

is_gobj(v)

Tells if the value is a gobj.

is_pure_number(v)

Tells if a string holds a number. It accepts an integer, a real number, the scientific form such as "1.2e3", and a sign.

empty_string(s)

Tells if a value is an empty string. A value that is not a string is empty too.

parseBoolean(v)

Changes a string into a boolean. It accepts true, on and 1 as true. It ignores the spaces around the value, and it ignores the case.

A json as a table: the flat form

One row per leaf: the id is the path of the item and the value is its value. It is the form to store, to compare and to diff, and it is the only one a person reads when two configurations disagree.

{"a": {"b": 1}, "c": [10, 20]}   ->   {"a`b": 1, "c`[0]": 10, "c`[1]": 20}

The grammar is identical to the C one, and it has to stay that way: a flat json is written by one side and read by the other. Ids are joined by the back-tick; a back-tick inside a key is doubled, so no key is forbidden; an array index is [N]; a key that starts with [ doubles the bracket; and an empty container is a leaf, because {} and [] have no leaves of their own. The C side is documented in kwid.h.

json2flat(jn)

Gives the flat form of a json: an object whose keys are the paths of the leaves and whose values are the leaves.

flat2json(flat)

Rebuilds the nested json of a flat object. It refuses instead of guessing when an id is a leaf and a container at once: the answer would depend on the order the ids are read in.

flat_key_join(segments)

Builds a flat id from its segments, escaping each one. A segment is a string for a key and a number for an array index.

flat_key_split(key)

The inverse: the segments of a flat id. An index comes back as a number and a key as a string — they are two types and not two spellings, which is what keeps the key "[0]" apart from the index 0.

flat_diff(flat1, flat2)

What changed between two flat objects: {added, removed, changed}.

flat_apply(flat, diff)

Applies to a flat object what flat_diff() reported. It works on the flat form on purpose: there an id addresses one value, so applying is setting and deleting, with nothing to walk and nothing to guess.