Using JSON in C/C++
by Sergey Lyubka, Cesanta
@CesantaHQ
What is JSON
{
"name": "sensor_1",
"wifi": {
"enable": true,
"ssid": "MyWifiNetwork",
"password": "ы"
},
"location": {
"lat": 53.3413712,
"lon": -6.2396965
}
}
- Simple human-readable format
- Keys can go in any order
- Strings are utf8. Newlines, non-printable chars are escaped.
What it is used for?
Most common use cases
- Storing configuration
- Easy to keep structure in a configuration, keeping it human-readable at the same time
- For example, Sublime Text editor stores its configuration in JSON
- Exchanging data between systems
- Communicating systems can be written using different technologies, e.g. backend in C++ and client application in JavaScript
- For example, web applications like Gmail use JSON to exchange data with browser
Using JSON in C/C++
- There is a C/C++ data structure
- This structure needs to be converted to/from JSON
{
"name": "sensor_1",
"wifi": {
"enable": true,
"ssid": "MyWifiNetwork",
"password": "ы"
},
"location": {
"lat": 53.3413712,
"lon": -6.2396965
}
}
/* Configuration structure */
struct config {
char *name;
struct {
int enable;
char *ssid;
char *pass;
} wifi;
struct {
double lat;
double lon;
} location;
};
Method 1: in-memory tree
- C/C++ do not have introspection capabilities (assuming no RTTI support)
- Therefore C/C++ JSON libraries build an intermediate representation of the JSON object in memory
- JSON libraries are listed on http://json.org
Intermediate tree API
/* Parsing JSON -> C/C++ structure */
struct config c;
struct json_obj = parse(json_string);
c.name = json_obj.attr("name").to_string();
c.location.lat = json_obj.attr("location").attr("lat").to_double();
...
/* Generating C/C++ structure -> JSON */
struct config c;
struct json_obj = init_json_obj();
json_obj.add_string("name", "my_device");
struct json_obj l = json_obj.add_emty_obj("location");
l.add_double("lat", 1.2345);
l.add_double("lon", -4,5678);
...
cout << json_obj.to_string();
Method 2: code generation
- The idea is: take C/C++ structure definition from a header file
- Run some external tool that parses C/C++ source file and creates code to convert in-memory C/C++ object into a JSON string and vice versa
- Pro:
- Handy C/C++ API for JSON marshalling
- No human error factor
- Contra:
- It could be painful to integrate an external tool into a build process - generated files, more complex build, a tool can drag dependencies, etc
- What if some structure fields need to be omitted?
Code generation example
/* file config.h : configuration structure */
struct config {
char *name;
double latitude;
};
static struct mem_layout {
const char *name;
size_t offset;
int type;
} config_mem_layout = {
{"name", offsetof(struct config, name), TYPE_STRING},
{"latitude", offsetof(struct config, latitude), TYPE_NUMBER}
};
char *config_to_json(const struct config *cfg) {
/* Having a pointer and memory layout, we can serialize */
...
}
Running a tool gives us marshalling API:
/* file config_json.h : marshalling API */
char *config_to_json(const struct config *);
Method 3: scanf/printf
- What if we want fetch values from JSON string directly into C/C++ variables?
- No intermediate tree?
- No code generation?
- Just like scanf/printf does?
Method 3: issues
- JSON keys can come in any order - scanf does not work
- JSON string values can be escaped
- It is hard to write printf/scanf for deeply nested structures
- But fear not. There is a library that can solve all of that.
Method 3: parsing
// str has the following JSON string (notice keys are out of order):
// { "a": 123, "c": true, "b": "hi" }
int a, b;
char *c;
json_scanf(str, strlen(str), "{ a:%d, b:%Q, c:%B }", &a, &b, &c);
// a == 123, b == "hi", c == true
- Extend scanf/printf with extra %B, %Q, %M
- Auto-escape keys and string values
Method 3: printing
json_printf(&out, "{%Q: %d, x: [%B, %B], y: %Q}", "foo", 123, 0, -1, "hi");
// Result:
// {"foo": 123, "x": [false, true], "y": "hi"}
Frozen JSON library
- Get it at https://github.com/cesanta/frozen
- Use it
Thank You!
contact me at
sergey.lyubka@cesanta.com
Using JSON in C/C++
By Sergey Lyubka
Using JSON in C/C++
- 3,035