cj is a tiny, flexible, and robust JSON parsing library for C.
cj is one header file and one source file. Running make
will build an object
file and library file. Alternatively, since it is so small, you may prefer to
add the source and header files directly into your project. cj is written in C89
and so is compabtile with compilers that only support C89.
To parse a JSON value using cj, simply call cj_parse
with pointers to the
allocator, reader, and location to store the resulting value. If cj_parse
returns CJ_SUCCESS
, then you may use the JSON value, and call cj_free
with
the same allocator and value to free memory allocated for it.
CJValue value;
if (cj_parse(&allocator, &reader, &value) == CJ_SUCCESS) {
do_something(&value);
cj_free(&allocator, &value);
} else {
printf("failed to parse\n");
}
cj requires you to provide interfaces for reading and allocating, and may provide default implementations if compiled with them. The interfaces contain a function pointer for the callback, which takes the interface as the first argument. From there you may access your own data to read from whatever data stream you desire.
/* create a buffer for read operations */
char b[128];
/* initialize a file reader */
CJFileReader r;
cj_init_file_reader(&r, f, b, sizeof(b));
/* use a pointer to the interface for parsing */
/* passing a NULL allocator uses the default allocator, if included */
CJParseResult result = cj_parse(NULL, &r.interface, &value);
cj is only a parsing library, and a small one at that. cj provides no methods for traversing the JSON tree, but its structures are easy to use.
/* example function - read thumbnail dimensions from json */
static bool read_dimensions(const CJValue *value, int *width, int *height) {
/* get object from root */
if (value->type != CJ_OBJECT) return false;
const CJObject *obj = &value->as.object;
/* invalid values for dimensions */
*width = *height = 0;
/* check members */
for (size_t i = 0; i < obj->length; ++i) {
/* get member */
const CJObjectMember *member = &obj->members[i];
/* get dimension based on key */
int *dim;
const CJString *key = &member->key;
if (key->length == 5 && strcmp(key->chars, "width") == 0) {
dim = width;
} else if (key->length == 6 && strcmp(key->chars, "height") == 0) {
dim = height;
} else continue;
/* must be number */
if (member->value.type != CJ_NUMBER) return false;
/* assign double to int, check overflow and that dim is positive */
*dim = member->value.as.number;
if (*dim < 1 || (double) *dim != member->value.as.number) return false;
}
/* check that both dimensions were assigned */
return *width != 0 && *height != 0;
}
if (read_dimensions(&thumbnail_info, &width, &height)) {
printf("thumbnail size is %dx%d\n", width, height);
}
Strings are null-terminated, but cj allows nulls ('\0'
) to appear anywhere in
strings. Where possible, use the length
field of CJString
instead of relying
on a null terminator.
cj only parses JSON encoded as UTF-8 without BOM. Surrogate pairs will be combined, and lone surrogates will be parsed as-is. Invalid UTF-8 will be rejected.
In order to avoid stack overflow and other crashes, cj sets a limit for how
deeply nested a JSON object may be. This value is defined by CJ_MAX_DEPTH
.
cj is thread safe, provided that:
- Your interfaces are also thread safe
- Your standard library's implementation of
setjmp
/longjmp
is thread safe
cj uses the parsing tests from the JSON Parsing Test Suite.
cj is licensed under the MIT License.