Mjson
C/C++ JSON parser, emitter, JSON-RPC engine for embedded systems
Install / Use
/learn @cesanta/MjsonREADME
mjson - a JSON parser + emitter + JSON-RPC engine
Features
- Small, ~1k lines of code, embedded-friendly
- No dependencies
- State machine parser, no allocations, no recursion
- High level API - fetch from JSON directly into C/C++ by jsonpath
- Low level SAX API
- Flexible JSON generation API - print to buffer, file, socket, etc
- JSON-RPC client/server. Connects any microcontroller online via https://vcon.io
Parsing example
const char *s = "{\"a\":1,\"b\":[2,false]}"; // {"a":1,"b":[2,false]}
double val; // Get `a` attribute
if (mjson_get_number(s, strlen(s), "$.a", &val)) // into C variable `val`
printf("a: %g\n", val); // a: 1
const char *buf; // Get `b` sub-object
int len; // into C variables `buf,len`
if (mjson_find(s, strlen(s), "$.b", &buf, &len)) // And print it
printf("%.*s\n", len, buf); // [2,false]
int v; // Extract `false`
if (mjson_get_bool(s, strlen(s), "$.b[1]", &v)) // into C variable `v`
printf("boolean: %d\n", v); // boolean: 0
Printing example
// Print into a statically allocated buffer
char buf[100];
mjson_snprintf(buf, sizeof(buf), "{%Q:%d}", "a", 123);
printf("%s\n", buf); // {"a":123}
// Print into a dynamically allocated string
char *s = mjson_aprintf("{%Q:%g}", "a", 3.1415);
printf("%s\n", s); // {"a":3.1415}
free(s); // Don't forget to free an allocated string
JSON-RPC example
In the following example, we initialize JSON-RPC context, and call
a couple of JSON-RPC methods: a built-in rpc.list method which lists
all registered methods, and our own foo method.
The sender() implementation just prints the reply to the standard output,
but in real life it should send a reply to the real remote peer - UART, socket,
or whatever else.
#include "mjson.h"
// A custom RPC handler. Many handlers can be registered.
static void foo(struct jsonrpc_request *r) {
double x;
mjson_get_number(r->params, r->params_len, "$[1]", &x);
jsonrpc_return_success(r, "{%Q:%g,%Q:%Q}", "x", x, "ud", r->userdata);
}
// Sender function receives a reply frame and must forward it to the peer.
static int sender(char *frame, int frame_len, void *privdata) {
printf("%.*s\n", frame_len, frame); // Print the JSON-RPC reply to stdout
return frame_len;
}
int main(void) {
jsonrpc_init(NULL, NULL);
// Call rpc.list
char request1[] = "{\"id\": 1, \"method\": \"rpc.list\"}";
jsonrpc_process(request1, strlen(request1), sender, NULL, NULL);
// Call non-existent method
char request2[] = "{\"id\": 1, \"method\": \"foo\"}";
jsonrpc_process(request2, strlen(request2), sender, NULL, NULL);
// Register our own function
char request3[] = "{\"id\": 2, \"method\": \"foo\",\"params\":[0,1.23]}";
jsonrpc_export("foo", foo);
jsonrpc_process(request3, strlen(request3), sender, NULL, (void *) "hi!");
return 0;
}
Build options
-D MJSON_ENABLE_PRINT=0disable emitting functionality, default: enabled-D MJSON_MAX_DEPTH=30define max object depth, default: 20-D MJSON_ENABLE_BASE64=0disable base64 parsing/printing, default: enabled-D MJSON_ENABLE_RPC=0disable RPC functionality, default: enabled-D MJSON_DYNBUF_CHUNK=256sets the allocation granularity ofmjson_print_dynamic_buf-D MJSON_ENABLE_PRETTY=0disablemjson_pretty(), default: enabled-D MJSON_ENABLE_MERGE=0disablemjson_merge(), default: enabled-D MJSON_ENABLE_NEXT=0disablemjson_next(), default: enabled-D MJSON_REALLOC=my_reallocredefine realloc() used bymjson_print_dynamic_buf(), default: realloc
Parsing API
mjson_find()
int mjson_find(const char *s, int len, const char *path, const char **tokptr, int *toklen);
In a JSON string s, len, find an element by its JSONPATH path.
Save found element in tokptr, toklen.
If not found, return JSON_TOK_INVALID. If found, return one of:
MJSON_TOK_STRING, MJSON_TOK_NUMBER, MJSON_TOK_TRUE, MJSON_TOK_FALSE,
MJSON_TOK_NULL, MJSON_TOK_ARRAY, MJSON_TOK_OBJECT. If a searched key
contains ., [ or ] characters, they should be escaped by a backslash.
Example:
// s, len is a JSON string: {"foo": { "bar": [ 1, 2, 3] }, "b.az": true}
char *p;
int n;
assert(mjson_find(s, len, "$.foo.bar[1]", &p, &n) == MJSON_TOK_NUMBER);
assert(mjson_find(s, len, "$.b\\.az", &p, &n) == MJSON_TOK_TRUE);
assert(mjson_find(s, len, "$", &p, &n) == MJSON_TOK_OBJECT);
mjson_get_number()
int mjson_get_number(const char *s, int len, const char *path, double *v);
In a JSON string s, len, find a number value by its JSONPATH path and
store into v. Return 0 if the value was not found, non-0 if found and stored.
Example:
// s, len is a JSON string: {"foo": { "bar": [ 1, 2, 3] }, "baz": true}
double v = 0;
mjson_get_number(s, len, "$.foo.bar[1]", &v); // v now holds 2
mjson_get_bool()
int mjson_get_bool(const char *s, int len, const char *path, int *v);
In a JSON string s, len, store value of a boolean by its JSONPATH path
into a variable v. Return 0 if not found, non-0 otherwise. Example:
// s, len is a JSON string: {"foo": { "bar": [ 1, 2, 3] }, "baz": true}
bool v = mjson_get_bool(s, len, "$.baz", false); // Assigns to true
mjson_get_string()
int mjson_get_string(const char *s, int len, const char *path, char *to, int sz);
In a JSON string s, len, find a string by its JSONPATH path and unescape
it into a buffer to, sz with terminating \0.
If a string is not found, return -1.
If a string is found, return the length of unescaped string. Example:
// s, len is a JSON string [ "abc", "de\r\n" ]
char buf[100];
int n = mjson_get_string(s, len, "$[1]", buf, sizeof(buf)); // Assigns to 4
mjson_get_hex()
int mjson_get_hex(const char *s, int len, const char *path, char *to, int sz);
In a JSON string s, len, find a string by its JSONPATH path and
hex decode it into a buffer to, sz with terminating \0.
If a string is not found, return -1.
If a string is found, return the length of decoded string.
The hex string should be lowercase, e.g. string Hello is hex-encoded as
"48656c6c6f". Example:
// s, len is a JSON string [ "48656c6c6f" ]
char buf[100];
int n = mjson_get_hex(s, len, "$[0]", buf, sizeof(buf)); // Assigns to 5
mjson_get_base64()
int mjson_get_base64(const char *s, int len, const char *path, char *to, int sz);
In a JSON string s, len, find a string by its JSONPATH path and
base64 decode it into a buffer to, sz with terminating \0.
If a string is not found, return 0.
If a string is found, return the length of decoded string. Example:
// s, len is a JSON string [ "MA==" ]
char buf[100];
int n = mjson_get_base64(s, len, "$[0]", buf, sizeof(buf)); // Assigns to 1
mjson()
int mjson(const char *s, int len, mjson_cb_t cb, void *cbdata);
Parse JSON string s, len, calling callback cb for each token. This
is a low-level SAX API, intended for fancy stuff like pretty printing, etc.
mjson_next()
int mjson_next(const char *s, int n, int off, int *koff, int *klen, int *voff,
int *vlen, int *vtype);
Assuming that JSON string s, n contains JSON object or JSON array,
return the next key/value pair starting from offset off.
key is returned as koff (key offset), klen (key length), value is returned as voff (value offset),
vlen (value length), vtype (value type). Pointers could be NULL.
Return next offset. When iterating over the array, koff will hold value
index inside an array, and klen will be 0. Therefore, if klen holds
0, we're iterating over an array, otherwise over an object.
Note: initial offset should be 0.
Usage example:
const char *s = "{\"a\":123,\"b\":[1,2,3,{\"c\":1}],\"d\":null}";
int koff, klen, voff, vlen, vtype, off;
for (off = 0; (off = mjson_next(s, strlen(s), off, &koff, &klen,
&voff, &vlen, &vtype)) != 0; ) {
printf("key: %.*s, value: %.*s\n", klen, s + koff, vlen, s + voff);
}
Emitting API
The emitting API is flexible and can print to anything: fixed buffer, dynamic growing buffer, FILE *, network socket, etc etc. The printer function gets the pointer to the buffer to print, and a user-specified data:
typedef int (*mjson_print_fn_t)(const char *buf, int len, void *userdata);
mjson library defines the following built-in printer functions:
struct mjson_fixedbuf {
char *ptr;
int size, len;
};
int mjson_print_fixed_buf(const char *ptr, int len, void *userdata);
int mjson_print_file(const char *ptr, int len, void *userdata);
int mjson_print_dynamic_buf(const char *ptr, int len, void *userdata);
If you want to print to something else, for example to a network socket,
define your own printing function. If you want to see usage examples
for the built-in printing functions, see unit_test.c file.
mjson_printf()
int mjson_vprintf(mjson_print_fn_t, void *, const char *fmt, va_list ap);
int mjson_printf(mjson_print_fn_t, void *, const char *fmt, ...);
Print using printf()-like format string. Supported specifiers are:
%Qprint quoted escaped string. Expect NUL-terminatedchar *%.*Qprint quoted escaped string. Expectint, char *%sprint string as is. Expect NUL-terminatedchar *%.*sprint string as is. Expectint, char *%g, print floating point number,
