The JSON module implements a family of routines that allow encoding, decoding and validation of JSON objects as defined in RFC 4627.
A NUL-terminated string in JSON syntax is decoded into its parse tree by means of the u_json_decode function as the showed in the following snippet:
u_json_t *jo = NULL; const char *i2 = "[ [ 1, 0 ], [ 0, 1 ] ]"; dbg_err_if (u_json_decode(i2, &jo));
Should you just need to check the supplied string for syntax compliance (i.e. without actually creating the syntax tree), the u_json_validate interface can be used:
char status[U_LEXER_ERR_SZ]; if (u_json_validate(i2, status)) u_con("Syntax error: %s", status);
You should not use the validating interface on untrusted input. In fact no maximum nesting depth is enforced on validation -- on the contrary, the parsing interface has the compile-time define U_JSON_MAX_DEPTH for such purpose -- so a malicious user could make your application stack explode by simply supplying a big-enough string made by all '
[' chars. The intended use of the validating interface is for checking your hand-crafted JSON strings before pushing them out, i.e. those you've created without going through all the u_json_new -> u_json_add -> u_json_encode chain.
Once the string has been parsed and (implicitly or explicitly) validated, should your application request frequent and/or massive access to its deeply nested attributes, then you may want to create an auxiliary index to the parse tree via u_json_index. This way its nodes can be accessed via a unique (and unambiguous -- provided that no anonymous key is embedded into the JSON object) naming scheme, similar to the typical struct/array access in C, i.e.:
So, for example, the string ".I2[0][0]" would retrieve the value "1" from "{ "I2": [ [ 1, 0 ], [ 0, 1 ] ] }".
long l; dbg_err_if (u_json_index(jo)); u_json_cache_get_int(jo, ".[0][0]", &l); // l = 1 u_json_cache_get_int(jo, ".[0][1]", &l); // l = 0 u_json_cache_get_int(jo, ".[1][0]", &l); // l = 0 u_json_cache_get_int(jo, ".[1][1]", &l); // l = 1
Please note that when index'ed, the parse tree enters a "frozen" state in which nothing but values and types of non-container objects (i.e. string, number, boolean and null's) can be changed. So, if you want to come back to full tree manipulation, you must remove the indexing structure by means of u_json_deindex -- which invalidates any subsequent cached access attempt.
JSON objects can be created ex-nihil via the u_json_new_* family of functions, and then encoded in their serialized form via the u_json_encode interface:
char *s = NULL; u_json_t *root = NULL, *leaf = NULL; dbg_err_if (u_json_new_object(NULL, &root)); dbg_err_if (u_json_new_int("integer", 999, &leaf)); dbg_err_if (u_json_add(root, leaf)); leaf = NULL; // encode it, should give: "{ "integer": 999 }" u_json_encode(root, &s);
The last basic concept that the user needs to know to work effectively with the JSON module is iteration. Iterators allow efficient and safe traversal of container types (i.e. arrays and objects), where a naive walk strategy based on u_json_array_get_nth would instead lead to performance collapse as access time is quadratic in the number of elements in the container.
long i, e; u_json_it_t jit; u_json_t *jo = NULL, *cur; const char *s = "[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]"; dbg_err_if (u_json_decode(s, &jo)); // init array iterator from first element and go forward. dbg_err_if (u_json_it(u_json_child_first(jo), &jit)); for (i = 1; (cur = u_json_it_next(&jit)) != NULL; i++) { dbg_err_if (u_json_get_int(cur, &e)); dbg_err_if (e != i); // e = 1..10 }
The following implementation choices have been made (re Section 4. of RFC 4627):
MAY | Answer | Notes |
---|---|---|
Accept non-JSON forms or extensions ? | NO | Trailing non-JSON text is allowed (though warned) at the end of string. I.e. the input string in scanned until the outermost container is matched. |
Set limits on the size of accepted texts ? | NO | Depending only upon the memory available to the parsing process. |
Set limits on the maximum depth of nesting ? | YES | Made available to the parsing interface through the compile-time constant U_JSON_MAX_DEPTH. The validating interface ignores this limit, and as such should be used with care (i.e. never on untrusted input). |
Set limits on the range of numbers ? | NO | All numerical values are stored as strings. Truncation/conversion issues can arise only when trying to extract their long or double counterparts through the u_json_get_int or u_json_get_real commodity interfaces. In case they fail you can still access the original (C-string) value through u_json_get_val. |
Set limits on the length and character contents of strings ? | YES | String length for both keys and values are upper-bounded by the compile-time constant U_TOKEN_SZ. |
Data Structures | |
struct | u_json_it_t |
Opaque iterator for traversing arrays and objects. More... | |
Defines | |
#define | U_JSON_MAX_DEPTH 16 |
Size of access keys to JSON values (can be changed at compile time via -DU_JSON_FQN_SZ=nnn flag). | |
Typedefs | |
typedef struct u_json_s | u_json_t |
Internal representation of any JSON object. | |
Enumerations | |
enum | { U_JSON_WALK_PREORDER, U_JSON_WALK_POSTORDER } |
Walk strategy when traversing JSON objects. More... | |
enum | u_json_type_t { , U_JSON_TYPE_STRING, U_JSON_TYPE_NUMBER, U_JSON_TYPE_OBJECT, U_JSON_TYPE_ARRAY, U_JSON_TYPE_TRUE, U_JSON_TYPE_FALSE, U_JSON_TYPE_NULL } |
JSON base types. More... | |
Functions | |
int | u_json_decode (const char *json, u_json_t **pjo) |
Break down a JSON string into pieces. | |
int | u_json_encode (u_json_t *jo, char **ps) |
Encode a JSON object. | |
int | u_json_validate (const char *json, char status[U_LEXER_ERR_SZ]) |
Validate the supplied JSON string. | |
int | u_json_index (u_json_t *jo) |
Index JSON object contents. | |
int | u_json_unindex (u_json_t *jo) |
Remove cache from JSON object. | |
u_json_t * | u_json_cache_get (u_json_t *jo, const char *name) |
Retrieve JSON node by its cache name. | |
const char * | u_json_cache_get_val (u_json_t *jo, const char *name) |
Wrapper around u_json_cache_get to retrieve string values from terminal (i.e. non-container) objects. | |
int | u_json_cache_get_int (u_json_t *jo, const char *name, long *pval) |
Wrapper around u_json_cache_get to retrieve integer values from terminal (i.e. non-container) objects. | |
int | u_json_cache_get_real (u_json_t *jo, const char *name, double *pval) |
Wrapper around u_json_cache_get to retrieve double precision FP values from terminal (i.e. non-container) objects. | |
int | u_json_cache_get_bool (u_json_t *jo, const char *name, char *pval) |
Wrapper around u_json_cache_get to retrieve boolean values from terminal (i.e. non-container) objects. | |
int | u_json_cache_set_tv (u_json_t *jo, const char *name, u_json_type_t type, const char *val) |
Set JSON node's type and value by its cache name. | |
int | u_json_new (u_json_t **pjo) |
Create a new and empty JSON object container. | |
void | u_json_free (u_json_t *jo) |
Dispose any resource allocated to a JSON object. | |
int | u_json_new_object (const char *key, u_json_t **pjo) |
Wrapper function that creates an object container (key may be NULL ). | |
int | u_json_new_array (const char *key, u_json_t **pjo) |
Wrapper function that creates an array container. (key may be NULL ). | |
int | u_json_new_string (const char *key, const char *val, u_json_t **pjo) |
Create new JSON string object. | |
int | u_json_new_number (const char *key, const char *val, u_json_t **pjo) |
Create new JSON number object. | |
int | u_json_new_real (const char *key, double val, u_json_t **pjo) |
Create new JSON number object from double precision FP number. | |
int | u_json_new_int (const char *key, long val, u_json_t **pjo) |
Create new JSON number object from long integer. | |
int | u_json_new_null (const char *key, u_json_t **pjo) |
Create new JSON null object. | |
int | u_json_new_bool (const char *key, char val, u_json_t **pjo) |
Create new JSON true or false object. | |
int | u_json_set_key (u_json_t *jo, const char *key) |
Set the key of a JSON object. | |
int | u_json_set_val (u_json_t *jo, const char *val) |
u_json_set_val_ex wrapper with no val syntax check | |
int | u_json_set_type (u_json_t *jo, u_json_type_t type) |
Set the type of a JSON object. | |
int | u_json_add (u_json_t *head, u_json_t *jo) |
Add a child JSON object to its parent container. | |
int | u_json_remove (u_json_t *jo) |
Remove an object from its JSON container. | |
const char * | u_json_get_val (u_json_t *jo) |
Get the value associated with the non-container object jo . | |
int | u_json_get_int (u_json_t *jo, long *pl) |
Get the long int value of the non-container object jo . | |
int | u_json_get_real (u_json_t *jo, double *pd) |
Get the double precision FP value of the non-container object jo . | |
int | u_json_get_bool (u_json_t *jo, char *pb) |
Get the boolean value of the non-container object jo . | |
u_json_t * | u_json_child_first (u_json_t *jo) |
Return the first child (if any) from the supplied container jo . | |
u_json_t * | u_json_child_last (u_json_t *jo) |
Return the last child (if any) from the supplied container jo . | |
unsigned int | u_json_array_count (u_json_t *jo) |
Return the number of elements in array jo , or 0 on error. | |
u_json_t * | u_json_array_get_nth (u_json_t *jo, unsigned int n) |
Get n-th element from jo array. | |
void | u_json_print (u_json_t *jo) |
Print to stderr the internal representation of a JSON object. | |
void | u_json_walk (u_json_t *jo, int strategy, size_t l, void(*cb)(u_json_t *, size_t, void *), void *cb_args) |
Pre/post-order tree walker. | |
int | u_json_it (u_json_t *jo, u_json_it_t *it) |
Initialize the iterator at it initially attached to jo . | |
u_json_t * | u_json_it_next (u_json_it_t *it) |
Get JSON element under cursor and step cursor in the forward direction. | |
u_json_t * | u_json_it_prev (u_json_it_t *it) |
Get JSON element under cursor and step cursor in the backwards direction. |
#define U_JSON_MAX_DEPTH 16 |
anonymous enum |
enum u_json_type_t |
Add the child JSON object jo
to its parent container head
.
head | Pointer to the parent container | |
jo | Pointer to the child JSON object that shall be attached |
~0 | on failure | |
0 | on success |
Definition at line 478 of file srcs/toolbox/json.c.
References U_JSON_TYPE_ARRAY.
Possibly retrieve a JSON node by its (fully qualified, or relative) cache name
.
jo | Pointer to the u_json_t object that must be searched | |
name | name of the element that must be searched |
NULL
in case key
was not found Definition at line 739 of file srcs/toolbox/json.c.
References u_hmap_easy_get(), and u_snprintf().
Referenced by u_json_array_get_nth(), u_json_cache_get_bool(), u_json_cache_get_int(), u_json_cache_get_real(), u_json_cache_get_val(), and u_json_cache_set_tv().
int u_json_cache_set_tv | ( | u_json_t * | jo, | |
const char * | name, | |||
u_json_type_t | type, | |||
const char * | val | |||
) |
Set type and value of the supplied JSON (leaf) node by its (fully qualified or relative) cache name
. If type
is U_JSON_TYPE_UNKNOWN the underlying type will be left untouched.
jo | Pointer to an u_json_t object | |
name | name of the element that must be set | |
type | new type (or U_JSON_TYPE_UNKNOWN if no type change) | |
val | new value. MUST be non-NULL in case we are setting a string a or number. |
0 | on success | |
~0 | on failure |
Definition at line 781 of file srcs/toolbox/json.c.
References u_json_cache_get(), U_JSON_TYPE_ARRAY, U_JSON_TYPE_NUMBER, U_JSON_TYPE_OBJECT, U_JSON_TYPE_STRING, and u_strlcpy().
int u_json_decode | ( | const char * | json, | |
u_json_t ** | pjo | |||
) |
Parse and (implicitly) validate the supplied JSON string json
. In case of success, its internal representation is returned into the result argument *pjo
.
json | A NUL-terminated string containing some serialized JSON | |
pjo | Result argument which will point to the internal representation of the parsed json string |
~0 | on failure | |
0 | on success |
Definition at line 516 of file srcs/toolbox/json.c.
int u_json_encode | ( | u_json_t * | jo, | |
char ** | ps | |||
) |
Encode the supplied JSON object jo
to the result string pointed by *ps
jo | Pointer to the u_json_t object that must be encoded | |
ps | serialized JSON text corresponding to jo |
~0 | on failure | |
0 | on success |
Definition at line 581 of file srcs/toolbox/json.c.
References u_string_create(), u_string_detach_cstr(), and u_string_free().
void u_json_free | ( | u_json_t * | jo | ) |
Dispose any resource allocated to the supplied JSON object jo
jo | Pointer to the u_json_t object that must be free'd |
Definition at line 549 of file srcs/toolbox/json.c.
References u_hmap_easy_free(), u_json_walk(), and U_JSON_WALK_POSTORDER.
Referenced by u_json_remove().
int u_json_index | ( | u_json_t * | jo | ) |
Index all contents of the supplied u_json_t top-level object jo
. After data has been indexed, no more key/type modifications are possible on this object; instead, values of leaf nodes can still be changed. Also, either child node addition nor removal is possible after the object has been cached. If jo
needs to be changed in aforementioned ways (type, key, addition or removal), it must be explicitly u_json_unindex'ed.
jo | Pointer to the u_json_t object that must be indexed |
Definition at line 670 of file srcs/toolbox/json.c.
References u_hmap_easy_free(), u_hmap_easy_new(), U_HMAP_OPTS_DATATYPE_POINTER, u_hmap_opts_free(), u_hmap_opts_new(), u_hmap_opts_set_val_freefunc(), u_hmap_opts_set_val_type(), u_json_walk(), and U_JSON_WALK_PREORDER.
int u_json_new | ( | u_json_t ** | pjo | ) |
Create a new and empty JSON object container and return its handler as the result argument *pjo
pjo | Pointer to the u_json_t that will be returned |
~0 | on failure | |
0 | on success |
Definition at line 301 of file srcs/toolbox/json.c.
References u_free(), and u_zalloc().
void u_json_print | ( | u_json_t * | jo | ) |
Print to stderr the supplied JSON object jo
jo | Pointer to the u_json_t object that must be printed |
Definition at line 646 of file srcs/toolbox/json.c.
References u_json_walk(), and U_JSON_WALK_PREORDER.
int u_json_remove | ( | u_json_t * | jo | ) |
Remove an object from its JSON container. This interface falls back to u_json_free in case the supplied jo
is the root node.
jo | Pointer to the u_json_t object that must be removed |
0 | on success | |
~0 | on failure |
Definition at line 991 of file srcs/toolbox/json.c.
References u_json_free(), and U_JSON_TYPE_ARRAY.
int u_json_set_key | ( | u_json_t * | jo, | |
const char * | key | |||
) |
Set the key of the JSON object jo
to the string value pointed by key
.
jo | Pointer to a u_json_t object | |
key | Pointer to the key string. In case key NULL the value is reset to the empty string. |
~0 | on failure | |
0 | on success |
Definition at line 443 of file srcs/toolbox/json.c.
References u_strlcpy().
int u_json_set_type | ( | u_json_t * | jo, | |
u_json_type_t | type | |||
) |
Set the type of the supplied JSON object jo
to type
.
jo | Pointer to a u_json_t object | |
type | One of the available u_json_type_t types |
~0 | on failure | |
0 | on success |
Definition at line 336 of file srcs/toolbox/json.c.
References U_JSON_TYPE_ARRAY, U_JSON_TYPE_FALSE, U_JSON_TYPE_NULL, U_JSON_TYPE_NUMBER, U_JSON_TYPE_OBJECT, U_JSON_TYPE_STRING, and U_JSON_TYPE_TRUE.
int u_json_unindex | ( | u_json_t * | jo | ) |
Remove the whole cacheing machinery from the previously u_json_index'd u_json_t object jo
.
jo | Pointer to the u_json_t object that must be de-indexed |
0 | on success | |
~0 | on failure |
Definition at line 716 of file srcs/toolbox/json.c.
References u_hmap_easy_free().
int u_json_validate | ( | const char * | json, | |
char | status[U_LEXER_ERR_SZ] | |||
) |
Validate the supplied JSON string json
. In case json
contains invalid syntax, the parser/lexer error message is returned into status
.
json | A NUL-terminated string containing some serialized JSON | |
status | In case of error, this result argument will contain an explanation message from the parser/lexer. |
~0 | on failure | |
0 | on success |
Definition at line 534 of file srcs/toolbox/json.c.
void u_json_walk | ( | u_json_t * | jo, | |
int | strategy, | |||
size_t | l, | |||
void(*)(u_json_t *, size_t, void *) | cb, | |||
void * | cb_args | |||
) |
Traverse the supplied JSON object jo
in pre/post-order, depending on strategy
, invoking the callback function cb
on each node.
jo | Pointer to u_json_t object to traverse | |
strategy | one of U_JSON_WALK_PREORDER or U_JSON_WALK_POSTORDER | |
l | depth level in the JSON tree (the root is at depth 0) | |
cb | function to invoke on each traversed node | |
cb_args | optional opaque data which will be supplied to cb |
Definition at line 613 of file srcs/toolbox/json.c.
References U_JSON_WALK_POSTORDER, and U_JSON_WALK_PREORDER.
Referenced by u_json_free(), u_json_index(), and u_json_print().