Skip to content

Instantly share code, notes, and snippets.

@pt300
Created December 27, 2018 18:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pt300/17441e85594428f7e2cb2a58cef2540d to your computer and use it in GitHub Desktop.
Save pt300/17441e85594428f7e2cb2a58cef2540d to your computer and use it in GitHub Desktop.
JSMN parse example using parent links feature
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define JSMN_PARENT_LINKS
#include "jsmn.h"
#define MAX_TOK 50
struct obj {
struct {
char *architecture;
size_t sqm;
} house;
struct {
char *brand;
size_t horsepower;
} car;
};
void fill_object(char *str, jsmntok_t *tokens, struct obj *parsed, int toknum) {
int i, parent;
char *tmp;
/*
* Iterate over tokens to find the keys of root object.
* Iteration is started from 1st object as 0th is root object.
* We know that key is the key of root object if it's `parent` value
* is set to 0.
*/
for(i = 1; i < toknum; i++) {
if(tokens[i].parent != 0 ||
tokens[i].type != JSMN_STRING)
continue;
/*
* compare key name and also check if there's a value attached to the key
* and also whether that value is an object and whether that object is empty
*/
if(strncmp("house", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0
&& tokens[i].size == 1) {
if(tokens[i + 1].size == 0 ||
tokens[i + 1].type != JSMN_OBJECT)
continue;
parent = i + 1;
/*
* Go over tokens untill we either hit end of them or arrive back at
* root object
*/
for(i++; i < toknum && tokens[i].parent != 0; i++) {
if(strncmp("architecture", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0) {
/*
* we check parent to make sure that we are not about to
* work on value of key that is part of unhandled key/value
*/
if(tokens[i].parent != parent ||
tokens[i].size != 1 ||
tokens[i + 1].type != JSMN_STRING)
continue;
i++; //now points at the value
/*
* Using calloc will zero out memory and with that also add
* null at the end of the string in an inefficient way, heh
*/
parsed->house.architecture = calloc(1, tokens[i].end - tokens[i].start + 1);
memcpy(parsed->house.architecture, &str[tokens[i].start], tokens[i].end - tokens[i].start);
}
else if(strncmp("sqm", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0) {
if(tokens[i].parent != parent ||
tokens[i].size != 1 ||
tokens[i + 1].type != JSMN_PRIMITIVE)
continue;
i++;
tmp = calloc(1, tokens[i].end - tokens[i].start + 1);
memcpy(tmp, &str[tokens[i].start], tokens[i].end - tokens[i].start);
parsed->house.sqm = atoi(tmp);
free(tmp);
}
}
i--; //without this the upper loop with increase the index by one and miss the key
}
else if(strncmp("car", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0) {
if(tokens[i + 1].size == 0 ||
tokens[i + 1].type != JSMN_OBJECT)
continue;
parent = i + 1;
for(i++; i < toknum && tokens[i].parent != 0; i++) {
if(strncmp("brand", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0) {
if(tokens[i].parent != parent ||
tokens[i].size != 1 ||
tokens[i + 1].type != JSMN_STRING)
continue;
i++;
parsed->car.brand = calloc(1, tokens[i].end - tokens[i].start + 1);
memcpy(parsed->car.brand, &str[tokens[i].start], tokens[i].end - tokens[i].start);
}
else if(strncmp("horsepower", &str[tokens[i].start], tokens[i].end - tokens[i].start) == 0) {
if(tokens[i].parent != parent ||
tokens[i].size != 1 ||
tokens[i + 1].type != JSMN_PRIMITIVE)
continue;
i++;
tmp = calloc(1, tokens[i].end - tokens[i].start + 1);
memcpy(tmp, &str[tokens[i].start], tokens[i].end - tokens[i].start);
parsed->car.horsepower = atoi(tmp);
free(tmp);
}
}
i--;
}
}
}
int main(void) {
jsmn_parser parser;
jsmntok_t tok[MAX_TOK];
int ret;
char *input = "{\n"
"\"house\": {\"architecture\": \"wood_frame\", \"sqm\": 100},\n"
"\"car\": {\"brand\": \"\", \"horsepower\": 40} \n"
"}";
struct obj parsed;
/*
* Initialize parser and parse JSON string
*/
jsmn_init(&parser);
ret = jsmn_parse(&parser, input, strlen(input), tok, sizeof tok / sizeof *tok);
if(ret < 0) {
fprintf(stderr, "Parsing failed (%i)\n", ret);
return ret;
}
/*
* Fill struct based on parsed data
*/
fill_object(input, tok, &parsed, ret);
printf("House:\n\tArchitecture: %s\n\tSqm: %i\nCar:\n\tBrand: %s\n\tHorsepower: %i\n",
parsed.house.architecture, parsed.house.sqm, parsed.car.brand, parsed.car.horsepower);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment