digip.org blog

Jansson 2.1 released

By Petri Lehtinen on 2011-06-11

Jansson 2.1 was released yesterday. This release adds a few new features and fixes some minor bugs. The full release notes are available here.

New features

A new decoding function, json_loadb(), was added for decoding buffers with length. The most important thing is that the input buffer need not be null terminated. In the future, it may also help to implement the support for zero bytes inside strings.

json_loadb() is like json_loads(), except that it takes an additional length argument:

value = json_loadb(buffer, length, 0, &error);

This version also introduces two new decoding flags and one new encoding flag:

  • JSON_REJECT_DUPLICATES: Issue a decoding error if any JSON object in the input contins duplicate keys.
  • JSON_DISABLE_EOF_CHECK: Stop decoding after a valid JSON input. This allows other data after the JSON data.
  • JSON_ENCODE_ANY: Allow encoding any JSON value. Without this flag, only arrays and objects can be encoded as the root value.

Bugfixes

  • Fix a memory leak when memory allocation fails in json_object_set() and friends.
  • Clear errno before calling strtod() for better portability (MINGW in this case).
  • Avoid set-but-not-used warning/error when building with the newest GCC.

Jansson 2.0.1 released

By Petri Lehtinen on 2011-04-01

Jansson 2.0.1 is out. This release fixes a few bugs, some of them major and some minor.

The most important bug fixes are:

  • Fix strict key checking in json_unpack() code. The strict mode (JSON_STRICT flag and the ! format character), checking that every object key is unpacked, didn't really work at all.
  • Fix the return value of json_object_size(). On error, it now returns 0, as documented. This may affect users if someone is relying on the buggy old return value of (size_t)-1.
  • Fix a few segfaulters when custom memory management is used.
  • Make the JANSSON_VERSION_HEX constant usable (by fixing the number of closing parentheses).

For full details, see the changelog. Special thanks to Eric Roy, Akos Polster and others who found bugs and provided patches!

New features of Jansson 2.0, part 3

By Petri Lehtinen on 2011-03-08

This post is the last one in a series of articles that give insight to the new features of Jansson 2.0; see part 1 and part 2.

Integer type

Up to version 1.3, the underlying type of JSON integer values was int. This approach limited the available numeric range and caused problems when using the Twitter API, for example, as it uses 64 bit integer IDs.

To overcome these issues, the underlying integer type was changed to the widest signed integer available in the system, i.e. long long if it's supported, falling back to plain long for older systems. The json_int_t typedef defines the actual type. The preprocessor constant JSON_INTEGER_IS_LONG_LONG is set to 1 if long long is supported, and JSON_INTEGER_FORMAT can be used for printing json_int_t values:

json_t *myint = json_integer(123);
printf("%" JSON_INTEGER_FORMAT "\n", json_integer_value(myint));

int should still be used in most cases when dealing with smallish JSON integers, as the compiler handles implicit type coercion. Only when the full 64-bit range is needed, json_int_t should be explicitly used.

Error reporting enhancements

New fields were added to the json_error_t struct that is used to pass error information to the caller. The following table lists all the fields. New fields in version 2.0 are marked with (new).

char text[] UTF-8 error message
char source[] Error source (e.g. file name) (new)
int line Input line
int column Character column on the input line (new)
int position Number of bytes from the beginning of the input (new)

column is the Unicode character column on which the error was encountered, i.e. a multi-byte UTF-8 character in input is treated as one character column. This may make it hard to debug Unicode problems. To remedy this, position gives the byte position of the error from the start of the input.

source is a string that contains the source of the error. When decoding, it is "<string>" when using json_loads(), "<stream>" when using json_loadf(), and the input file name when using json_load_file().

The json_error_t struct is also used to return errors from json_unpack_ex() and json_pack_ex(). In this case, line, column and position point to the position in the format string on which the error occured. source is "<format>" when the format string is invalid, "<args>" when there are problems with the variadic arguments (e.g. NULL string argument or invalid UTF-8 string), "<validation>" when the value being unpacked doesn't validate against the format string, or <internal> when an internal error occurs (e.g. out of memory).

Library version

Preprocessor constans defining the Jansson library version were added:

JANSSON_VERSION_MAJOR, JANSSON_VERSION_MINOR, JANSSON_VERSION_MICRO
Integers specifying the major, minor and micro versions, respectively.
JANSSON_VERSION
A string representation of the current version, e.g. "1.2.1" or "1.3". When micro version is zero, it's omitted from the version string.
JANSSON_VERSION_HEX
A 3-byte hexadecimal representation of the version, e.g. 0x010201 for version 1.2.1 and 0x010300 for version 1.3. This is useful in numeric comparisions, e.g.:
#if JANSSON_VERSION_HEX >= 0x010300
/* Code specific to version 1.3 and above */
#endif

Custom memory allocation

A function to set custom memory allocation functions was added. For example, to use the Boehm's conservative garbage collector, use

json_set_alloc_funcs(GC_malloc, GC_free);

Quite obviously, json_set_alloc_funcs() needs to be called before any other Jansson API function to make sure that all values are allocated and freed with the same set of functions.

Tags: jansson

New features of Jansson 2.0, part 2

By Petri Lehtinen on 2011-03-02

This post continues a series of articles that give insight to the new features of Jansson 2.0; see part 1.

This article is about the json_pack() API. It allows the user to build arbitrary JSON values using a simple format string-based approach. As with json_unpack(), the idea has been stolen from Python's C API.

Here are some examples:

json_pack("i", 42);
/* -> JSON integer 42 */

json_pack("{s: s, s: i}", "foo", "bar", "baz", 123);
/* -> JSON object {"foo": "bar", "baz": 123} */

json_pack("{s: [{s: i}, {s: i}]}", "data", "value", 15, "value", 16);
/* -> JSON object {"data": [{"value": 15}, {"value": 16}]} */

The first argument is a format string that describes the type and structure of the value that's being built. The format i creates an integer and s means a string (both as a value and an object key). {} and [] are used to build objects and arrays. Whitespace, : and , are ignored. As the last example shows, objects and arrays can be nested, there is no limit on the nesting depth. json_pack() returns the new JSON value, or NULL on error.

json_pack_ex() is also available. It makes it possible to get error messages and pass flags, although currently no flags are defined. Example:

json_t *value;
json_error_t error;

value = json_pack_ex(&error, 0, "[iii]", 1, 2, 3);
if(!value) {
    fprintf(stderr, "Error: %d:%d: %s\n", error.line, error.column, error.text);
    return -1;  /* error */
}

/* ... */

json_decref(value);

The errors that may occur are problems with the format string (e.g. unbalanced } or an invalid format character), out of memory errors and invalid UTF-8 in strings, so this function is mainly useful for debugging format strings.

json_pack() provides a powerful means of creating JSON values, both simple and complex. Without it, you might need tens of lines of code with ugly temporary variables to make nested objects or arrays. I think this is a great addition to Jansson's API and will make it a whole lot easier to create JSON data in C.

For full details of format characters, see the documentation.

Tags: jansson

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