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 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 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!
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.
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.