I recently had to parse Json data on a small embedded system. When looking for tools to help with this, I found and loved Serge Zaitsev’s jsmn (pronounced “Jasmine”).
Jsmn is written in C and considered one if not the most efficient Json parser in the field. Its philosophy is to be as simple as possible: no dynamic memory allocation, no callbacks, and absolutely no dependences. It’s a great piece of software, but it is a bit light on examples especially for parsing bigger json data structures. I got stuck and asked a question on how others do structure their parsers but so far nobody shared a working solution.
I needed to split the Json parser into multiple functions. Mostly because I do not like big junks of code and I need to be able to unit-test smaller pieces of my code like the specialized parser functions.
Some sample Json data to parse:
{
"sense": [
{
"name": "inside",
"component": "bme280"
},
{
"name": "outside",
"component": "bme280",
"params": ["119", "120"]
}
],
"user": "mark",
"unex": "pected"
}
I studied all jsmn samples on the web I could find but did not find a simple solution I liked so I had to come up with something myself. The solution I came up with is really, really simple - in the past this almost always proved to be a reliable indicator for a good solution. So I wrote this post to share it with you.
First I parse the Json data into an array of tokens:
int i = 0;
int r;
jsmn_parser p;
jsmntok_t tokens[128]; /* We expect no more than 128 tokens */
// parsed the string into tokens using jsmn
jsmn_init(&p);
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), tokens, sizeof(tokens)/sizeof(tokens[0]));
if (r < 0)
{
printf("Failed to parse JSON: %d\n", r);
return EXIT_FAILURE;
}
Now I process the array with specialized parser functions like here:
parse_sense_object(&i, tokens);
parse_actor_object(&i, tokens);
Each parser function advances the index i into the tokens array. Since C has no pass-by-reference I need to hand in a pointer to i.
The test case prints out a lot of information so it is easy to see what is going on from the output of running the test case on esp32:
...
Running jsmn demo split parser into smaller parts test...
root_size: 21
- Sense:
size: 2
o Instance:
size: 2
i: 4; k: 0
Name: inside
i: 6; k: 1
Component: bme280
o Instance:
size: 3
i: 9; k: 0
Name: outside
i: 11; k: 1
Component: bme280
i: 13; k: 2
Params:
size: 2
* 119
* 120
Unexpected key: unex
Unexpected key: pected
/home/mark/devel/10_esp32/esp32/jsmn_demo/test/main/test_app.c:159:jsmn demo split parser into smaller parts test:PASS
I commited the complete sample code to my github - feel free to take a look.
I hope this post is helpful to you.
All the best, Mark