A simple JSON parser
git clone git@soophie.de:/srv/git/libjson
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..38e8f95
--- /dev/null
+++ b/Makefile
1
@@ -0,0 +1,18 @@2
+default: build3
+4
+build:5
+ cc -o libjson.so -shared -fPIC -lpthread -lssl -Wall -Wextra libjson.c6
+7
+install:8
+ cc -o libjson.so -shared -fPIC libjson.c9
+ sudo cp libjson.h /usr/local/include/10
+ sudo cp libjson.so /usr/local/lib/11
+ sudo ldconfig12
+ rm libjson.so13
+14
+uninstall:15
+ sudo rm /usr/local/include/libjson.h16
+ sudo rm /usr/local/lib/libjson.so17
+18
+clean:19
+ rm libjson.so
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5f7ce93
--- /dev/null
+++ b/README.md
1
@@ -0,0 +1,9 @@2
+# libjson3
+4
+A simple JSON parser5
+6
+## Installation7
+8
+```9
+sudo make install10
+```
diff --git a/libjson.c b/libjson.c
new file mode 100644
index 0000000..66a09ac
--- /dev/null
+++ b/libjson.c
1
@@ -0,0 +1,203 @@2
+#include <stdio.h>3
+#include <stdlib.h>4
+#include <stdbool.h>5
+#include <string.h>6
+#include <ctype.h>7
+#include <inttypes.h>8
+9
+#include "libjson.h"10
+11
+char *json_str(json_ptr_t json_ptr) {12
+ jsmntok_t token = json_ptr.tokens[json_ptr.ptr];13
+ int token_len = token.end - token.start;14
+ char *str = malloc(token_len + 1);15
+ memcpy(str, json_ptr.str + token.start, token_len);16
+ str[token_len] = '\0';17
+ return str;18
+}19
+20
+json_ptr_t json_ptr(jsmntok_t *tokens, int len, const char *str) {21
+ return (json_ptr_t) {22
+ .tokens = tokens,23
+ .len = len,24
+ .ptr = 0,25
+ .str = str,26
+ .err = JSON_OK,27
+ };28
+}29
+30
+json_ptr_t json_err(json_ptr_t json_ptr, json_err_t json_err) {31
+ json_ptr.err = json_err;32
+ return json_ptr;33
+}34
+35
+int json_size(json_ptr_t json_ptr) {36
+ return json_ptr.tokens[json_ptr.ptr].size;37
+}38
+39
+json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str) {40
+ jsmn_parser parser;41
+ jsmn_init(&parser);42
+ int len = jsmn_parse(&parser, str, strlen(str), tokens, tokens_len);43
+ json_ptr_t ptr = json_ptr(tokens, len, str);44
+ if (len < 0) {45
+ return json_err(ptr, JSON_ERR_INVALID_FORMAT);46
+ }47
+ return ptr;48
+}49
+50
+/**51
+ Increments the `jsmntok_t` pointer until the child `JSMN_OBJECT` or52
+ `JSMN_ARRAY` is traversed and the pointer points to the next entry.53
+ If no errors occured, the return code is `0`.54
+*/55
+void json_seek(json_ptr_t *json_ptr) {56
+ jsmntok_t token_root = json_ptr->tokens[json_ptr->ptr];57
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {58
+ json_err(*json_ptr, JSON_ERR_INVALID_TYPE);59
+ return;60
+ }61
+ jsmntok_t token;62
+ for (int i = 0; i < token_root.size; i++) {63
+ if (token_root.type == JSMN_OBJECT) {64
+ token = json_ptr->tokens[++json_ptr->ptr]; // key65
+ }66
+ token = json_ptr->tokens[++json_ptr->ptr]; // value67
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {68
+ json_seek(json_ptr);69
+ if (json_ptr->err) {70
+ return;71
+ }72
+ }73
+ }74
+}75
+76
+json_ptr_t json_get(json_ptr_t json_ptr, const char *key) {77
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];78
+ if (token_root.type != JSMN_OBJECT) {79
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);80
+ }81
+ jsmntok_t token;82
+ int token_len;83
+ while (json_ptr.ptr < json_ptr.len) {84
+ token = json_ptr.tokens[++json_ptr.ptr]; // key85
+ token_len = token.end - token.start;86
+ char token_key[token_len + 1];87
+ memcpy(token_key, json_ptr.str + token.start, token_len);88
+ token_key[token_len] = '\0';89
+ if (strncmp(json_ptr.str + token.start, key, token_len) == 0) {90
+ json_ptr.ptr++;91
+ return json_ptr;92
+ }93
+ token = json_ptr.tokens[++json_ptr.ptr]; // value94
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {95
+ json_seek(&json_ptr);96
+ if (json_ptr.err) {97
+ return json_ptr;98
+ }99
+ }100
+ }101
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);102
+}103
+104
+json_ptr_t json_at(json_ptr_t json_ptr, int idx) {105
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];106
+ if (token_root.type != JSMN_ARRAY) {107
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);108
+ }109
+ if (idx >= token_root.size) {110
+ return json_err(json_ptr, JSON_ERR_INVALID_SIZE);111
+ }112
+ jsmntok_t token;113
+ int token_idx = 0;114
+ while (json_ptr.ptr < json_ptr.len) {115
+ token = json_ptr.tokens[++json_ptr.ptr];116
+ if (token_idx == idx) {117
+ return json_ptr;118
+ }119
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {120
+ json_seek(&json_ptr);121
+ if (json_ptr.err) {122
+ return json_ptr;123
+ }124
+ }125
+ token_idx++;126
+ }127
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);128
+}129
+130
+json_ptr_t json_query(json_ptr_t json_ptr, const char *query) {131
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];132
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {133
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);134
+ }135
+ jsmntok_t token;136
+ int i = 0;137
+ int query_len = strlen(query);138
+ while (i < query_len) {139
+ token = json_ptr.tokens[json_ptr.ptr];140
+ char c = query[i];141
+ if (c == '.') {142
+ if (token.type != JSMN_OBJECT) {143
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_OBJECT144
+ }145
+ i++;146
+ int j = i;147
+ bool found = false;148
+ for (; j < query_len; j++) {149
+ if (!isalpha(query[j])) {150
+ int key_len = j - i;151
+ if (key_len == 0) {152
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);153
+ }154
+ char key[key_len + 1];155
+ memcpy(key, query + i, key_len);156
+ key[key_len] = '\0';157
+ json_ptr = json_get(json_ptr, key);158
+ i += key_len;159
+ found = true;160
+ break;161
+ }162
+ }163
+ if (!found) {164
+ int key_len = j - i;165
+ if (key_len == 0) {166
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);167
+ }168
+ char key[key_len + 1];169
+ memcpy(key, query + i, key_len);170
+ key[key_len] = '\0';171
+ json_ptr = json_get(json_ptr, key);172
+ i += key_len;173
+ }174
+ continue;175
+ }176
+ if (c == '[') {177
+ if (token.type != JSMN_ARRAY) {178
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_ARRAY179
+ }180
+ i++;181
+ for (int j = i; j < query_len; j++) {182
+ if (query[j] == ']') {183
+ int idx_len = j - i;184
+ if (idx_len == 0) {185
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);186
+ }187
+ char idx[idx_len + 1];188
+ memcpy(idx, query + i, idx_len);189
+ idx[idx_len] = '\0';190
+ int index = strtoimax(idx, NULL, 10);191
+ json_ptr = json_at(json_ptr, index);192
+ i += idx_len + 1;193
+ break;194
+ }195
+ }196
+ continue;197
+ }198
+ if (!isalpha(c)) {199
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); // invalid char200
+ }201
+ i++;202
+ }203
+ return json_ptr;204
+}
diff --git a/libjson.h b/libjson.h
new file mode 100644
index 0000000..027d7a6
--- /dev/null
+++ b/libjson.h
1
@@ -0,0 +1,26 @@2
+#pragma once3
+4
+#include <jsmn.h>5
+6
+typedef enum {7
+ JSON_OK = 0,8
+ JSON_ERR_INVALID_TYPE = 1,9
+ JSON_ERR_INVALID_SIZE = 2,10
+ JSON_ERR_INVALID_FORMAT = 3,11
+ JSON_ERR_INVALID_MISSING = 4,12
+} json_err_t;13
+14
+typedef struct {15
+ jsmntok_t *tokens;16
+ int len;17
+ int ptr;18
+ const char *str;19
+ json_err_t err;20
+} json_ptr_t;21
+22
+char *json_str(json_ptr_t json_ptr);23
+int json_size(json_ptr_t json_ptr);24
+json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str);25
+json_ptr_t json_get(json_ptr_t json_ptr, const char *key);26
+json_ptr_t json_at(json_ptr_t json_ptr, int idx);27
+json_ptr_t json_query(json_ptr_t json_ptr, const char *query);