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