digip.org blog

New features of Jansson 2.0, part 1

By Petri Lehtinen on 2011-03-01

This post starts a series of articles that give insight to the new features of Jansson 2.0.

First up is the json_unpack() API. I think it's the most powerful new feature, allowing the user to perform two things on a JSON value: data extraction, and validation against a simple schema. The idea has been stolen from Python's C API.

Example:

/* Assume that obj is the following JSON object:
 *   {"x": 15.4, "y": 99.8, "z": 42}}
 */
json_t *obj;
double x, y, z;

if(json_unpack(obj, "{s:f, s:f, s:f}", "x", &x, "y", &y, "z", &z))
    return -1;  /* error */

assert(x == 15.4 && y == 99.8 && z == 42);

The format string passed to json_unpack() describes the structure of the object. The s format denotes an object key, and the f format means a real number value. Whitespace, : and , are ignored, so {sfsfsf} would be an equivalent format string to the one above.

After the format string, there's one argument for each format character. For object keys, a string specifies what key is accessed, and for real numbers, a pointer to double gives an address where to store the value.

The equivalent code without json_unpack() would be something like this:

json_t *obj, *tmp;
double x, y, x;

tmp = json_object_get(obj, "x");
if(!json_is_real(tmp))
    return -1;  /* error */
x = json_real_value(tmp);

/* repeat for y and z */
/* ... */

printf("x: %f, y: %f, z: %f\n", x, y, z);
/* ==> x: 15.4, y: 99.8, z: 42 */

The code that uses json_unpack() is much shorter and cleaner, and it's easier to see what it's doing.

Nested values are supported, too:

/* Assume that nested is the following JSON object:
 *   {"foo": {"bar": [11, 12, 13]}}
 */
json_t *nested;
int i1, i2, i3;

if(json_unpack(nested, "{s:{s:[iii]}}", "foo", "bar", &i1, &i2, &i3))
    return -1;  /* error */

assert(i1 == 11 && i2 == 12 && i3 == 13);

This time, the format string has two nested objects and a nested array. There's no limit on the nesting levels. The variable arguments are used in the "flat" order in which they appear in the format string.

The same API can also be used in a validation-only mode, i.e. without extracting any values. Error messages are also available:

/* Assume the same JSON object as in the previous example */
json_t *nested;
json_error_t error;

if(json_unpack_ex(nested, &error, JSON_VALIDATE_ONLY,
   "{s:{s:[iii]}}", "foo", "bar"))
{
    fprintf(stderr, "Error: %d:%d: %s\n", error.line, error.column, error.text);
    return -1;
}

The json_unpack_ex() function is the extended version of json_unpack(). It takes an error parameter, similar to decoding functions, and optional flags to control the behaviour. The JSON_VALIDATE_ONLY flags tells it to only validate and not to extract anything. Extra arguments after the format sting are only required for object keys. The available validation is quite simple, only the object/array structure and value types can be checked, but usually this saves a lot of code.

I strongly believe that this feature, along with the json_pack() API described in the next part, will make it an order of magnitude more pleasant to manipulate JSON data in C. Many thanks to Graeme Smecher for suggesting this and providing the initial implementation.

This article only gave a few examples. For full details, all available format characters and flags, see the documentation.

Tags: jansson