All Projects → cesanta → Mjson

cesanta / Mjson

Licence: mit
C/C++ JSON parser, emitter, JSON-RPC engine for embedded systems

Programming Languages

c
50402 projects - #5 most used programming language

Projects that are alternatives of or similar to Mjson

Co
Art of C++. Flag, logging, unit-test, json, go-style coroutine and more.
Stars: ✭ 2,264 (+1564.71%)
Mutual labels:  json-rpc, json
Getty
a netty like asynchronous network I/O library based on tcp/udp/websocket; a bidirectional RPC framework based on JSON/Protobuf; a microservice framework based on zookeeper/etcd
Stars: ✭ 532 (+291.18%)
Mutual labels:  json-rpc, json
Jsonrpc C
JSON-RPC in C (server only for now)
Stars: ✭ 155 (+13.97%)
Mutual labels:  json-rpc, embedded
Autoserver
Create a full-featured REST/GraphQL API from a configuration file
Stars: ✭ 188 (+38.24%)
Mutual labels:  json-rpc, json
Ejdb
🏂 EJDB 2.0 — Embeddable JSON Database engine C library. Simple XPath like query language (JQL). Websockets / Android / iOS / React Native / Flutter / Java / Dart / Node.js bindings. Docker image.
Stars: ✭ 1,187 (+772.79%)
Mutual labels:  json, embedded
Jsonrpc
The jsonrpc package helps implement of JSON-RPC 2.0
Stars: ✭ 143 (+5.15%)
Mutual labels:  json-rpc, json
Leopotamgrouplibraryunity
Tools library for unity 3d game engine: animator graph helpers, serialization (json), localization, event routing (eventbus, ui actions), embedded scripting, uGui xml markup, threading, tweening, in-memory protection and other helpers (pure C#)
Stars: ✭ 373 (+174.26%)
Mutual labels:  json, mit
Criterion
Microbenchmarking for Modern C++
Stars: ✭ 140 (+2.94%)
Mutual labels:  json, mit
Python Ripple Lib
Python client for the Ripple API
Stars: ✭ 23 (-83.09%)
Mutual labels:  json-rpc, json
Libjson Rpc Cpp
C++ framework for json-rpc (json remote procedure call)
Stars: ✭ 653 (+380.15%)
Mutual labels:  json-rpc, json
Arduinojson
📟 JSON library for Arduino and embedded C++. Simple and efficient.
Stars: ✭ 5,456 (+3911.76%)
Mutual labels:  json, embedded
Tiny Json
The tiny-json is a versatile and easy to use json parser in C suitable for embedded systems. It is fast, robust and portable.
Stars: ✭ 127 (-6.62%)
Mutual labels:  json, embedded
Unqlite
An Embedded NoSQL, Transactional Database Engine
Stars: ✭ 1,583 (+1063.97%)
Mutual labels:  json, embedded
Szl
A lightweight, embeddable scripting language
Stars: ✭ 134 (-1.47%)
Mutual labels:  mit, embedded
Gojsonq
A simple Go package to Query over JSON/YAML/XML/CSV Data
Stars: ✭ 1,790 (+1216.18%)
Mutual labels:  json
D3r
d3.js helpers for R
Stars: ✭ 133 (-2.21%)
Mutual labels:  json
Nerves
Craft and deploy bulletproof embedded software in Elixir
Stars: ✭ 1,778 (+1207.35%)
Mutual labels:  embedded
Quicktype Vscode
VS Code extension to convert JSON to TypeScript, C#, Go, and many more
Stars: ✭ 131 (-3.68%)
Mutual labels:  json
Aws Lambda Scala
Writing AWS Lambdas in Scala
Stars: ✭ 135 (-0.74%)
Mutual labels:  json
Gloss
[Deprecated] A shiny JSON parsing library in Swift ✨ Loved by many from 2015-2021
Stars: ✭ 1,648 (+1111.76%)
Mutual labels:  json

mjson - a JSON parser + emitter + JSON-RPC engine

Build Status License: MIT Code Coverage

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]}

// Extract value of `a` into a variable `val` and print its value
double val;
if (mjson_get_number(s, strlen(s), "$.a", &val)) printf("a: %g\n", val);

// Extract sub-object `b` and print it:
const char *sub;
int len;
if (mjson_find(s, strlen(s), "$.b", &sub, &len)) printf("%.*s\n", len, sub);

// Extract `false`:
int boolval;
if (mjson_get_bool(s, strlen(s), "$.b[1]", &boolval)) printf("%d\n", boolval);

Printing example

Print into a dynamically-allocated string:

char *result = NULL;      // It's important to initialise it to NULL
mjson_printf(mjson_print_dynamic_buffer, &result, "{%Q:%d}", "a", (int) 123);
printf("%s\n", result);   // {"a":123}
free(result);             // Caller must deallocate result

Print into some custom target, for example, a network socket:

// A custom "printer" function
static int myprint(const char *buf, int len, void *userdata) {
  int fd = * (int *) userdata;
  return send(fd, buf, len, 0);
}

// Print into file descriptor `fd` a string `{"data":"aGkh"}`
mjson_printf(myprint, &fd, "{%Q:%V}", "data", 3, "hi!");

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, (void *) "hi");
  jsonrpc_process(request3, strlen(request3), sender, NULL, NULL);

  return 0;
}

Build options

  • -D MJSON_ENABLE_PRINT=0 disable emitting functionality, default: enabled
  • -D MJSON_IMPLEMENT_STRTOD=1 use own strtod(), default: stdlib is used
  • -D MJSON_MAX_DEPTH=30 define max object depth, default: 20
  • -D MJSON_ENABLE_BASE64=0 disable base64 parsing/printing, default: enabled
  • -D MJSON_ENABLE_RPC=0 disable RPC functionality, default: enabled
  • -D MJSON_DYNBUF_CHUNK=256 sets the allocation granularity of mjson_print_dynamic_buf
  • -D MJSON_ENABLE_PRETTY=1 enable mjson_pretty(), default: disabled
  • -D MJSON_ENABLE_MERGE=1 enable mjson_merge(), default: disabled
  • -D MJSON_ENABLE_NEXT=1 enable mjson_next(), default: disabled

Parsing API

mjson_find()

enum mjson_tok 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. Example:

// s, len is a JSON string: {"foo": { "bar": [ 1, 2, 3] }, "baz": true} 
char *p;
int n;
assert(mjson_find(s, len, "$.foo.bar[1]", &p, &n) == MJSON_TOK_NUMBER);
assert(mjson_find(s, len, "$.baz", &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);

NOTE: to enable this function, use -D MJSON_ENABLE_NEXT=1.

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:

  • %Q print quoted escaped string. Expect NUL-terminated char *
  • %.*Q print quoted escaped string. Expect int, char *
  • %s print string as is. Expect NUL-terminated char *
  • %.*s print string as is. Expect int, char *
  • %g, %f print floating point number. Expect double
  • %d, %u print signed/unsigned integer. Expect int
  • %ld, %lu print signed/unsigned long integer. Expect long
  • %B print true or false. Expect int
  • %V print quoted base64-encoded string. Expect int, char *
  • %H print quoted hex-encoded string. Expect int, char *
  • %M print using custom print function. Expect int (*)(mjson_print_fn_t, void *, va_list *)

The following example produces {"a":1, "b":[1234]} into the dynamically-growing string s. Note that the array is printed using a custom printer function:

static int m_printer(mjson_print_fn_t fn, void *fndata, va_list *ap) {
  int value = va_arg(*ap, int);
  return mjson_printf(fn, fndata, "[%d]", value);
}

...
char *s = NULL;
mjson_printf(&mjson_print_dynamic_buf, &s, "{%Q:%d, %Q:%M}", "a", 1, "b", m_printer, 1234);
/* At this point `s` contains: {"a":1, "b":[1234]}  */
free(s);

mjson_pretty()

int mjson_pretty(const char *s, int n, const char *pad,
                 mjson_print_fn_t fn, void *userdata);

NOTE: to enable this function, use -D MJSON_ENABLE_PRETTY=1.

Pretty-print JSON string s, n using padding pad. If pad is "", then a resulting string is terse one-line. Return length of the printed string.

mjson_merge()

int mjson_merge(const char *s, int n, const char *s2, int n2,
                mjson_print_fn_t fn, void *fndata);

NOTE: to enable this function, use -D MJSON_ENABLE_MERGE=1.

Merge JSON string s2,n2 into the original string s,n. Both strings are assumed to hold objects. The result is printed using fn,fndata. Return value: number of bytes printed.

In order to delete the key in the original string, set that key to null in the s2,n2. NOTE: both strings must not contain arrays, as merging arrays is not supported.

JSON-RPC API

For the example, see unit_test.c :: test_rpc() function.

jsonrpc_init

void jsonrpc_init(void (*response_cb)(const char *, int, void *),
                  void *response_cb_data);

Initialize JSON-RPC context. The sender() function must be provided by the caller, and it is responsible to send the prepared JSON-RPC reply to the remote side - to the UART, or socket, or whatever. The sender() function receives the full frame to send, and the privdata poitner.

The response_cb() function could be left NULL. If it is non-NULL, it will be called for all received responses generated by the jsonrpc_call(). The response_cb() function receives full response frame, and the privdata pointer.

jsonrpc_process

jsonrpc_process(const char *frame, int frame_len, jsonrpc_sender_t fn, void *fdata, void *userdata);

Parse JSON-RPC frame contained in frame, and invoke a registered handler. The userdata pointer gets passed as r->userdata to the RPC handler.

jsonrpc_export

#define jsonrpc_export(const char *name,
                       void (*handler)(struct jsonrpc_request *));

Export JSON-RPC function. A function gets called by jsonrpc_process(), which parses an incoming frame and calls a registered handler. A handler() receives struct jsonrpc_request *. It could use jsonrpc_return_error() or jsonrpc_return_success() for returning the result.

NOTE: a name is a glob pattern that follows these rules:

  • * matches 0 or more characters, excluding /
  • ? matches any character
  • # matches 0 or more characters
  • any other character matches itself

For example, after jsonrpc_export("Foo.*", my_func, my_data);, the server triggers my_func on Foo.Bar, Foo.Baz, etc.

struct jsonrpc_request

struct jsonrpc_request {
  struct jsonrpc_ctx *ctx;
  const char *params;     // Points to the "params" in the request frame
  int params_len;         // Length of the "params"
  const char *id;         // Points to the "id" in the request frame
  int id_len;             // Length of the "id"
  mjson_print_fn_t fn;    // Printer function
  void *fndata;           // Printer function data
  void *userdata;         // userdata pointer passed to jsonrpc_process()
};

This structure gets passed to the method callback.

jsonrpc_return_success

void jsonrpc_return_success(struct jsonrpc_request *r, const char *result_fmt, ...);

Return result from the method handler. NOTE: if the request frame ID is not specified, this function does nothing.

jsonrpc_return_error

void jsonrpc_return_error(struct jsonrpc_request *r, int code, const char *message, const char *data_fmt, ...);

Return error from the method handler. JSON-RPC error frame looks like this:

{"id":1, "error": {"code": -32602, "message": "Invalid params", "data": {"foo": "bar"}}}

The frame contains a error object with numeric code and string message keys, and an optional data which can be arbitrary - a simple JSON type, or an array/object. In the optional data, you can pass some extra information about the error, for example a faulty request.

NOTE: if the request frame ID is not specified, this function does nothing.

JSON-RPC Arduino example

#include "mjson.h"

// Gets called by the RPC engine to send a reply frame
static int sender(const char *frame, int frame_len, void *privdata) {
  return Serial.write(frame, frame_len);
}

// RPC handler for "Sum". Expect an array of two integers in "params"
static void sum(struct jsonrpc_request *r) {
  int a = mjson_get_number(r->params, r->params_len, "$[0]", 0);
  int b = mjson_get_number(r->params, r->params_len, "$[1]", 0);
  jsonrpc_return_success(r, "%d", a + b);
}

void setup() {
  jsonrpc_init(NULL, NULL);   // Initialise the library
  jsonrpc_export("Sum", sum, NULL);   // Export "Sum" function
  Serial.begin(115200);               // Setup serial port
}

static void handle_serial_input(unsigned char ch) {
  static char buf[256];  // Buffer that holds incoming frame
  static size_t len;     // Current frame length

  if (len >= sizeof(buf)) len = 0;  // Handle overflow - just reset
  buf[len++] = ch;                  // Append to the buffer
  if (ch == '\n') {                 // On new line, parse frame
    jsonrpc_process(buf, len, sender, NULL, NULL);
    len = 0;
  }
}

void loop() {
  char buf[800];
  if (Serial.available() > 0) {
    int len = Serial.readBytes(buf, sizeof(buf));
    jsonrpc_process(buf, len, sender, NULL, NULL);
  }
}

When this sketch is compiled and flashed on an Arduino board, start Arduino Serial Monitor, type {"id": 1, "method": "Sum", "params": [2,3]} and hit enter. You should see an answer frame:

Example - connect Arduino Uno to AWS IoT device shadow

See https://vcon.io for more information.

Contact

Please visit https://vcon.io/contact.html

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].