digip.org blog

Jansson is two years old

By Petri Lehtinen on 2011-08-25

Today is Jansson's birthday. The first release, Jansson 1.0, was released on August 25, 2009. At that time, I thought the library was substantially ready, and there would be only few or no new features to be added anymore. I was wrong.

Because I thought that the library was ready and mature, I was bold and gave it the version number 1.0. In open source software, it's quite common to have 0.x versions for years and years. The safety of 0.x versions lies in the illusion that you can break backwards compatibility in new versions. In my opinion it's like pulling the carpet from under the users' feet.

Quite soon it turned out that Jansson wasn't so mature and featureful after all. On Jansson's first birthday, version 1.3 had been out for two months and there were plenty of new features compared to 1.0. But there were a few problems that bugged me, and fixing them was not possible without breaking backwards compatibility.

It took a long time to make, but version 2.0 was finally released on February 28, 2011. It was the first, and hopefully last, backwards incompatible version, fixing design mistakes in the 1.x series. For the first time, I also tried to prepare for the future; the decoding functions got an extra, unused flags parameter for future needs. The preparing paid off, as version 2.1 already uses the new parameter, and we didn't need another backwards incompatible change to make it happen.

What's most remarkable, though, is that there are people out there who actually use Jansson to make awesome things happen! The library started out as a project to replace existing JSON libraries for C, just because none of them was appropriate for my needs. As time passed, it turned out that other people had similar needs, and that was the driving force for me to fix bugs, add new features, review patches, etc. Thank you everyone, without you there wouldn't be Jansson as we know it!

To celebrate the birthday, I was planning to release version 2.2 today. However, I've been extremely busy organizing PyCon Finland 2011 for the last weeks and months. I really hope to get 2.2 out soon.

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