A small footprint(code and memory) simple JSON parsing C library for embedded projects
A small footprint(code and memory) simple JSON parsing C library for embedded projects
# cmake -DJSON_64BITS_INTEGERS=OFF -DJSON_HEX_NUMBERS=OFF -DJSON_FLOATS=OFF.
# make
BUILD_SHARED_LIBRARY
(OFF) — Build shared library (not only static)JSON_64BITS_INTEGERS
(OFF) — Enable support of 64 bits integersJSON_HEX_NUMBERS
(OFF) — Enabled support of 0x integersJSON_FLOATS
(OFF) — Enable support of Floating point NumbersJSON_SHORT_NEXT
(OFF) — Use short
type for next field of jsn_tJSON_PACKED
(OFF) — Use packed json item structure
JSON_AUTO_PARSE_FN
(ON) — Build json_auto_parse() function
JSON_AUTO_PARSE_POOL_START_SIZE
(32) — Initial jsn_t array sizeJSON_AUTO_PARSE_POOL_INCREASE
(n+32) — Increase jsn_t array size formulaJSON_STRINGIFY_FN
(ON) — Build json_stringify() function
JSON_GET_FN
(ON) — Build json_get() function
JSON_MAX_ID_LENGTH
(64) — Maximum identifiers length in path for json_get functionBUILD_TESTS
(ON) — Build tests application
#include <nano/json.h>
typedef
enum {
JS_UNDEFINED=1, JS_NULL, JS_BOOLEAN, JS_NUMBER, JS_FLOAT, JS_STRING, JS_ARRAY, JS_OBJECT
} nj_type_t;
#ifdef JSON_64BITS_INTEGERS
typedef int64_t jsn_number_t;
#else
typedef int32_t jsn_number_t;
#endif
#ifdef JSON_SHORT_NEXT
typedef short jsn_next_t;
#else
typedef int jsn_next_t;
#endif
typedef
struct jsn {
union {
char *string; /* string id of the node in the parent object */
unsigned int number; /* integer index of the node in the parent array */
} id;
union {
jsn_number_t number;
char *string;
jsn_next_t length; /* number of object/array elements */
#ifdef JSON_FLOATS
double floating;
#endif
} data;
jsn_next_t next; /* index offset to next sibling node (0 - parent node offset) */
char id_type; /* type of id. JS_NUMBER(for array) or JS_STRING(for object) */
char type; /* type of data (nj_type_t) */
}
#ifdef JSON_PACKED
__attribute__((packed))
#endif
jsn_t;
int json_parse(jsn_t *pool, size_t size, char *text)
pool
— pointer to (somewhere allocated) array of jsn_t
elementssize
— number of pool elementstext
— JSON text source. Will be corrupted because all strings will be stored in this buffer.Parse JSON text to array of nodes. The first node is always root node. If parsing made succesfully
then source text memory will be used for storing of values identifiers and strings.
Please, don’t forget this.
The function return a number of used jsn_t
elements of array pointed by pool
argument.
On error, negative value is returned, and errno is set appropriately.
ENOMEM
passed array of jsn_t
elements is not enough for store parsed JSON data tree.EINVAL
impossible to parse passed JSON text. The returned negative value is offset to broken
jsn_t json[100];
int len = json_parse(json, sizeof json / sizeof json[0], text);
if (len < 0) {
perror("json_parse");
...
}
...
jsn_t *json_auto_parse(char *text, char **end)
text
— JSON text source. Will be corrupted because all strings will be stored in this buffer.end
— in case of parsing error by the .Parse JSON text
.
The function return a pointer to array jsn_t
elements. Should be released by free()
.
On error, NULL is returned, and errno is set appropriately.
ENOMEM
Out of memory.EINVAL
impossible to parse passed JSON text. If end
is not NULL, a pointer to brokenend
. The text buffer will not be corrupted by parser.
char *err_pos;
jsn_t *json = json_auto_parse(text, &err_pos);
if (!json) {
if (errno == EINVAL) {
char crop[50];
snprintf(crop, sizeof crop, "%s", err_pos);
perror("json_auto_parse: JSON syntax error at the code '%s'", crop);
} else
perror("json_auto_parse");
return -1;
}
...
free(json);
char *json_stringify(char *out, size_t size, jsn_t *root)
outbuf
— output buffer for JSON textsize
— size of output buffer including terminating zeroroot
— root element of json treeConvert parsed JSON tree back to text. May be useful for debugging purpose.
Return pointer to output buffer.
int source_len = strlen(source);
jsn_t *json = json_auto_parse(source, NULL);
if (!json)
return -1;
char string[source_len * 2];
json_stringify(string, sizeof string, json);
free(json);
printf("JSON: %s\n", string);
return 0;
jsn_t *json_item(jsn_t *node, char const *id)
node
— object json node to search elementid
— string identifier of object elementReturns element of object with id
or NULL if absent.
ENOENT
there is no element with id
value.ENOTDIR
the node
is not object type.jsn_t *json_cell(jsn_t *node, int index)
node
— array json node to search elementindex
— index of array elementReturns element of array with index
or NULL if absent.
ENOENT
there is no element with index
value.ENOTDIR
the node
is not array type.jsn_t *json_get(jsn_t *node, char const *path)
node
— array json node to search elementpath
— qualified path of JSON itemReturns element of json with path
or NULL if absent. If path
is empty string then returns the node
value.
ENOENT
there is no element in path
.ENOTDIR
wrong node type in path.EINVAL
impossible to parse passed path
.
char const json[] =
"{"
"\"0\":\"value\","
"\"key\":555,"
"\"array\":["
"0,1,2,3,4,5"
"],"
"\"obj\":{"
"\"ololo\":["
"\"a\",\"b\",{"
"\"key\":123"
"}"
"]"
"}"
"}";
jsn_t j[100];
int len = json_parse(j, sizeof j / sizeof j[0], json);
if (len < 0) {
perror("json_parse");
// ...
}
char const *z = json_string(json_get(j, "[\"0\"]")); // "value"
int n0 = json_number(json_get(j, ".key")); // 555
int n1 = json_number(json_get(j, "[\"key\"]")); // 555
jsn_t *array = json_get(j, ".array"); // [0,1,2,3,4,5]
int n2 = json_number(json_get(j, ".array[3]")); // 3
char const *z = json_string(json_get(j, ".obj.ololo[1]"); // "\"b\"",
jsn_t *ololo2 = json_get(j, ".obj.ololo[2]"); // "{\"key\":123}",
int n3 = json_number(json_get(j, ".obj.ololo[2].key"); // "123"
// ...
char const *json_string(jsn_t *node, char const *missed_value)
node
— pointer to json nodemissed_value
— default value if node is undefined (NULL)Returns pointer to string value of node. For non JS_STRING node will be casted
to static buffer string.
If node is NULL returns missed_value
.
int json_boolean(jsn_t *node, int missed_value)
node
— pointer to json nodemissed_value
— default value if node is undefined (NULL)Returns boolean(1 or 0) value of node. Non JS_BOOLEAN node will be casted
to boolean by JS type convertation rules.
If node is NULL returns missed_value
.
int json_number(jsn_t *node, int missed_value)
node
— pointer to json nodemissed_value
— default value if node is undefined (NULL)Returns integer value of node. Non JS_NUMBER node will be casted
by JS type convertation rules.
If node is NULL returns missed_value
.
double json_float(jsn_t *node, double missed_value)
node
— pointer to json nodemissed_value
— default value if node is undefined (NULL)Returns floating pointer(double) value of node. Non JS_FLOAT node will be casted
by JS type convertation rules.
If node is NULL returns missed_value
.
json_foreach
For enumerating of child nodes of JS_OBJECT/JS_ARRAY object you can use json_foreach
macro-definition.
jsn_t *obj = json_auto_parse("[1,2,3,4,5]");
json_foreach(obj, offset) {
jsn_t *node = obj + offset;
printf("obj[%d] = '%s'\n", node->id.index, json_string(node));
}
free(obj);
static int test_gets()
{
char ser[4096];
char *text = strdup("\
[\
{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"capabilities\",\"params\":{}},\
{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"ololo\",\"params\":523424}\
]");
printf("test_gets\n");
jsn_t json[100];
int len = json_parse(json, 100, text);
if (len < 0) {
perror("json_obj_scan parse");
free(text);
return 1;
}
printf("parsed %d nodes\n", len);
json_foreach(json, offset) {
jsn_t *node = json + offset;
char const *version = json_string(json_item(node, "jsonrpc"), "0");
char const *method = json_string(json_item(node, "method"), NULL);
int id = json_number(json_item(node, "id"), -1);
jsn_t *params = json_item(node, "params");
printf("[%d]: version: %s, id: %d, method: %s, params: %s\n",
node->id.index, version, id, method, json_stringify(ser, sizeof ser, params));
}
free(text);
return 0;
}