JSON parser and generator for C/C++ with scanf/printf like interface. Targeting embedded systems.
JSON parser and generator for C/C++ with scanf/printf like interface. Targeting embedded systems.
json_scanf() scans a string directly into C/C++ variablesjson_printf() prints C/C++ variables directly into an output streamjson_setf() modifies an existing JSON stringjson_fread() reads JSON from a filejson_fprintf() writes JSON to a filejson_scanf(), json_vscanfint json_scanf(const char *str, int str_len, const char *fmt, ...);
int json_vscanf(const char *str, int str_len, const char *fmt, va_list ap);
/* json_scanf's %M handler */
typedef void (*json_scanner_t)(const char *str, int len, void *user_data);Scans the JSON string str, performing scanf-like conversions according to fmt.
fmt uses scanf()-like format, with the following differences:
str is a JSON string { "a": 123, "b": "hi", c: true }.
We can fetch only the value of the c key:int value = 0;
json_scanf(str, strlen(str), "{c: %B}", &value);int * (or char *, if sizeof(bool) == sizeof(char)),
expects boolean true or false.char **, expects quoted, JSON-encoded string. Scanned
string is malloc-ed, caller must free() the string.char **, int *. Expects base64-encoded string.
Result string is base64-decoded, malloced and NUL-terminated.
The length of result string is stored in int * placeholder.
Caller must free() the result.int *, char **.
Expects a hex-encoded string, e.g. "fa014f".
Result string is hex-decoded, malloced and NUL-terminated.
The length of the result string is stored in int * placeholder.
Caller must free() the result.void *user_data parameter - see json_scanner_t definition.struct json_token *, fills it out with matched token.Returns the number of elements successfully scanned & converted. Negative number means scan error.
Example - scan arbitrary JSON string:
…Example - parse array of objects:
// str has the following JSON string - array of objects:
// { "a": [ {"b": 123}, {"b": 345} ] }
// This example shows how to iterate over array, and parse each object.
int i, value, len = strlen(str);
struct json_token t;
for (i = 0; json_scanf_array_elem(str, len, ".a", i, &t) > 0; i++) {
// t.type == JSON_TYPE_OBJECT
json_scanf(t.ptr, t.len, "{b: %d}", &value); // value is 123, then 345
}json_scanf_array_elem()int json_scanf_array_elem(const char *s, int len,
const char *path,
int index,
struct json_token *token);A helper function to scan an array item with given path and index.
Fills token with the matched JSON token.
Returns -1 if no array element found, otherwise non-negative token length.
json_printf()Frozen printing API is pluggable. Out of the box, Frozen provides a way to print to a string buffer or to an opened file stream. It is easy to tell Frozen to print to another destination, for example, to a socket, etc. Frozen does this by defining an "output context" descriptor which has a pointer to a low-level printing function. If you want to print to another destination, just define your specific printing function and initialise output context with it.
This is the definition of the output context descriptor:
struct json_out {
int (*printer)(struct json_out *, const char *str, size_t len);
union {
struct {
char *buf;
size_t size;
size_t len;
} buf;
void *data;
FILE *fp;
} u;
};Frozen provides two helper macros to initialise two built-in output descriptors:
struct json_out out1 = JSON_OUT_BUF(buf, len);
struct json_out out2 = JSON_OUT_FILE(fp);typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
int json_printf(struct json_out *, const char *fmt, ...);
int json_vprintf(struct json_out *, const char *fmt, va_list ap);Generate formatted output into a given string buffer, auto-escaping keys. This is a superset of printf() function, with extra format specifiers:
%B print json boolean, true or false. Accepts an int.%Q print quoted escaped string or null. Accepts a const char *.%.*Q same as %Q, but with length. Accepts int, const char *%V print quoted base64-encoded string. Accepts a const char *, int.%H print quoted hex-encoded string. Accepts a int, const char *.%M invokes a json_printf_callback_t function. That callback function
can consume more parameters.Return number of bytes printed. If the return value is bigger than the supplied buffer, that is an indicator of overflow. In the overflow case, overflown bytes are not printed.
Example:
json_printf(&out, "{%Q: %d, x: [%B, %B], y: %Q}", "foo", 123, 0, -1, "hi");
// Result:
// {"foo": 123, "x": [false, true], "y": "hi"}To print a complex object (for example, serialise a structure into an object),
use %M format specifier:
struct my_struct { int a, b; } mys = {1,2};
json_printf(&out, "{foo: %M, bar: %d}", print_my_struct, &mys, 3);
// Result:
// {"foo": {"a": 1, "b": 2}, "bar": 3}int print_my_struct(struct json_out *out, va_list *ap) {
struct my_struct *p = va_arg(*ap, struct my_struct *);
return json_printf(out, "{a: %d, b: %d}", p->a, p->b);
}json_printf_array()int json_printf_array(struct json_out *, va_list *ap);A helper %M callback that prints contiguous C arrays.
Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt
Returns number of bytes printed.
json_walk() - low level parsing API…json_walk() is a low-level, callback based parsing API.
json_walk() calls a given callback function for each scanned value.
Callback receives a name, a path to the value, a JSON token that points to the value and an arbitrary user data pointer.
The path is constructed using this rule:
. (dot) is appended to the path[ELEMENT_INDEX] is appended for each elementFor example, consider the following json string:
{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }.
The sequence of callback invocations will be as follows:
JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULLJSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123"JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULLJSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1"JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2"JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULLJSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true"JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": true }"JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { \"baz\": true } ]"JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, \"bar\": [ 1, 2, { \"baz\": true } ] }"If top-level element is an array: [1, {"foo": 2}]
JSON_TYPE_ARRAY_START, name: NULL, path: "", value: NULLJSON_TYPE_NUMBER, name: "0", path: "[0]", value: "1"JSON_TYPE_OBJECT_START, name: "1", path: "[1]", value: NULLJSON_TYPE_NUMBER, name: "foo", path: "[1].foo", value: "2"JSON_TYPE_OBJECT_END, name: NULL, path: "[1]", value: "{\"foo\": 2}"JSON_TYPE_ARRAY_END, name: NULL, path: "", value: "[1, {"foo": 2}]"If top-level element is a scalar: true
JSON_TYPE_TRUE, name: NULL, path: "", value: "true"json_walk_args() - low level parsing API extensible interfaceThis function is identical to json_walk() except that it takes a
struct pointer argument for the callback and callback_data
arguments and additional configuration elements:
struct frozen_args {
json_walk_callback_t callback;
void *callback_data;
int limit;
};This struct must be initialized using INIT_FROZEN_ARGS() to retain
forward compatibility before any members are set as illustrated here:
struct frozen_args args[1];
INIT_FROZEN_ARGS(args);
args->callback = mycb;
args->callback_data = data;
args->limit = 20;
ret = json_walk_args(string, len, args);the limit member of struct frozen_args can be set to limit the
maximum recursion depth to prevent possible stack overflows and limit
parsing complexity.
json_fprintf(), json_vfprintf()/*
* Same as json_printf, but prints to a file.
* File is created if does not exist. File is truncated if already exists.
*/
int json_fprintf(const char *file_name, const char *fmt, ...);
int json_vfprintf(const char *file_name, const char *fmt, va_list ap);json_asprintf(), json_vasprintf()/*
* Print JSON into an allocated 0-terminated string.
* Return allocated string, or NULL on error.
* Example:
*
* ```c
* char *str = json_asprintf("{a:%H}", 3, "abc");
* printf("%s\n", str); // Prints "616263"
* free(str);
* ```
*/
char *json_asprintf(const char *fmt, ...);
char *json_vasprintf(const char *fmt, va_list ap);json_fread()/*
* Read the whole file in memory.
* Return malloc-ed file content, or NULL on error. The caller must free().
*/
char *json_fread(const char *file_name);json_setf(), json_vsetf()…json_prettify()/*
* Pretty-print JSON string `s,len` into `out`.
* Return number of processed bytes in `s`.
*/
int json_prettify(const char *s, int len, struct json_out *out);json_prettify_file()/*
* Prettify JSON file `file_name`.
* Return number of processed bytes, or negative number of error.
* On error, file content is not modified.
*/
int json_prettify_file(const char *file_name);json_next_key(), json_next_elem()/*
* Iterate over an object at given JSON `path`.
* On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL
* for `key`, or `val`, in which case they won't be populated.
* Return an opaque value suitable for the next iteration, or NULL when done.
*
* Example:
*
* ```c
* void *h = NULL;
* struct json_token key, val;
* while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) {
* printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr);
* }
* ```
*/
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val);
/*
* Iterate over an array at given JSON `path`.
* Similar to `json_next_key`, but fills array index `idx` instead of `key`.
*/
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val);
By building with -DJSON_MINIMAL=1 footprint can be significantly reduced.
The following limitations apply in this configuration:
No open issues yet, or sync has not completed.