JSON


Overview

The JSON module implements a family of routines that allow encoding, decoding and validation of JSON objects as defined in RFC 4627.

Decoding

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));

Validating

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.

Indexing

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.

Building and Encoding

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);

Iterators

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
    }

RFC implementation

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_tu_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_tu_json_child_first (u_json_t *jo)
 Return the first child (if any) from the supplied container jo.
u_json_tu_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_tu_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_tu_json_it_next (u_json_it_t *it)
 Get JSON element under cursor and step cursor in the forward direction.
u_json_tu_json_it_prev (u_json_it_t *it)
 Get JSON element under cursor and step cursor in the backwards direction.

Define Documentation

#define U_JSON_MAX_DEPTH   16

Max nesting of JSON fields (can be changed at compile time via -DU_JSON_MAX_DEPTH=nnn flag)

Definition at line 58 of file json.h.


Enumeration Type Documentation

anonymous enum
Enumerator:
U_JSON_WALK_PREORDER 

pre-order tree walk

U_JSON_WALK_POSTORDER 

post-order tree walk

Definition at line 31 of file json.h.

Enumerator:
U_JSON_TYPE_STRING 

string type

U_JSON_TYPE_NUMBER 

number (i.e. int, frac or exp) type

U_JSON_TYPE_OBJECT 

object container

U_JSON_TYPE_ARRAY 

array container

U_JSON_TYPE_TRUE 

a boolean true value

U_JSON_TYPE_FALSE 

a boolean false value

U_JSON_TYPE_NULL 

an explicit null value

Definition at line 37 of file json.h.


Function Documentation

int u_json_add ( u_json_t head,
u_json_t jo 
)

Add the child JSON object jo to its parent container head.

Parameters:
head Pointer to the parent container
jo Pointer to the child JSON object that shall be attached
Return values:
~0 on failure
0 on success

Definition at line 478 of file srcs/toolbox/json.c.

References U_JSON_TYPE_ARRAY.

u_json_t * u_json_cache_get ( u_json_t jo,
const char *  name 
)

Possibly retrieve a JSON node by its (fully qualified, or relative) cache name.

Parameters:
jo Pointer to the u_json_t object that must be searched
name name of the element that must be searched
Returns:
the retrieved JSON (sub)object on success; 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.

Parameters:
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.
Return values:
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.

Parameters:
json A NUL-terminated string containing some serialized JSON
pjo Result argument which will point to the internal representation of the parsed json string
Return values:
~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

Parameters:
jo Pointer to the u_json_t object that must be encoded
ps serialized JSON text corresponding to jo
Return values:
~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

Parameters:
jo Pointer to the u_json_t object that must be free'd
Returns:
nothing

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.

Parameters:
jo Pointer to the u_json_t object that must be indexed
Returns:
nothing

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

Parameters:
pjo Pointer to the u_json_t that will be returned
Return values:
~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

Parameters:
jo Pointer to the u_json_t object that must be printed
Returns:
nothing

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.

Parameters:
jo Pointer to the u_json_t object that must be removed
Return values:
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.

Parameters:
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.
Return values:
~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.

Parameters:
jo Pointer to a u_json_t object
type One of the available u_json_type_t types
Return values:
~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.

Parameters:
jo Pointer to the u_json_t object that must be de-indexed
Return values:
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.

Parameters:
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.
Return values:
~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.

Parameters:
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
Returns:
nothing

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


←Products
© 2005-2012 - KoanLogic S.r.l. - All rights reserved