A simple JSON parser
git clone git@soophie.de:/srv/git/libjson
diff --git a/libjson.c b/libjson.c
deleted file mode 100644
index 66a09ac..0000000
--- a/libjson.c
+++ /dev/null
1
@@ -1,203 +0,0 @@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
index 027d7a6..1404c01 100644
--- a/libjson.h
+++ b/libjson.h
1
@@ -1,5 +1,9 @@2
-#pragma once3
+#ifndef LIB_JSON_H4
+#define LIB_JSON_H6
+#ifndef LIB_JSON_IMPL7
+#define JSMN_HEADER8
+#endif /* LIB_JSON_IMPL */9
#include <jsmn.h>11
typedef enum {12
@@ -24,3 +28,212 @@ json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str);13
json_ptr_t json_get(json_ptr_t json_ptr, const char *key);14
json_ptr_t json_at(json_ptr_t json_ptr, int idx);15
json_ptr_t json_query(json_ptr_t json_ptr, const char *query);16
+17
+#ifdef LIB_JSON_IMPL18
+19
+#include <stdio.h>20
+#include <stdlib.h>21
+#include <stdbool.h>22
+#include <string.h>23
+#include <ctype.h>24
+#include <inttypes.h>25
+26
+#include "libjson.h"27
+28
+char *json_str(json_ptr_t json_ptr) {29
+ jsmntok_t token = json_ptr.tokens[json_ptr.ptr];30
+ int token_len = token.end - token.start;31
+ char *str = malloc(token_len + 1);32
+ memcpy(str, json_ptr.str + token.start, token_len);33
+ str[token_len] = '\0';34
+ return str;35
+}36
+37
+json_ptr_t json_ptr(jsmntok_t *tokens, int len, const char *str) {38
+ return (json_ptr_t) {39
+ .tokens = tokens,40
+ .len = len,41
+ .ptr = 0,42
+ .str = str,43
+ .err = JSON_OK,44
+ };45
+}46
+47
+json_ptr_t json_err(json_ptr_t json_ptr, json_err_t json_err) {48
+ json_ptr.err = json_err;49
+ return json_ptr;50
+}51
+52
+int json_size(json_ptr_t json_ptr) {53
+ return json_ptr.tokens[json_ptr.ptr].size;54
+}55
+56
+json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str) {57
+ jsmn_parser parser;58
+ jsmn_init(&parser);59
+ int len = jsmn_parse(&parser, str, strlen(str), tokens, tokens_len);60
+ json_ptr_t ptr = json_ptr(tokens, len, str);61
+ if (len < 0) {62
+ return json_err(ptr, JSON_ERR_INVALID_FORMAT);63
+ }64
+ return ptr;65
+}66
+67
+/**68
+ Increments the `jsmntok_t` pointer until the child `JSMN_OBJECT` or69
+ `JSMN_ARRAY` is traversed and the pointer points to the next entry.70
+ If no errors occured, the return code is `0`.71
+*/72
+void json_seek(json_ptr_t *json_ptr) {73
+ jsmntok_t token_root = json_ptr->tokens[json_ptr->ptr];74
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {75
+ json_err(*json_ptr, JSON_ERR_INVALID_TYPE);76
+ return;77
+ }78
+ jsmntok_t token;79
+ for (int i = 0; i < token_root.size; i++) {80
+ if (token_root.type == JSMN_OBJECT) {81
+ token = json_ptr->tokens[++json_ptr->ptr]; // key82
+ }83
+ token = json_ptr->tokens[++json_ptr->ptr]; // value84
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {85
+ json_seek(json_ptr);86
+ if (json_ptr->err) {87
+ return;88
+ }89
+ }90
+ }91
+}92
+93
+json_ptr_t json_get(json_ptr_t json_ptr, const char *key) {94
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];95
+ if (token_root.type != JSMN_OBJECT) {96
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);97
+ }98
+ jsmntok_t token;99
+ int token_len;100
+ while (json_ptr.ptr < json_ptr.len) {101
+ token = json_ptr.tokens[++json_ptr.ptr]; // key102
+ token_len = token.end - token.start;103
+ char token_key[token_len + 1];104
+ memcpy(token_key, json_ptr.str + token.start, token_len);105
+ token_key[token_len] = '\0';106
+ if (strncmp(json_ptr.str + token.start, key, token_len) == 0) {107
+ json_ptr.ptr++;108
+ return json_ptr;109
+ }110
+ token = json_ptr.tokens[++json_ptr.ptr]; // value111
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {112
+ json_seek(&json_ptr);113
+ if (json_ptr.err) {114
+ return json_ptr;115
+ }116
+ }117
+ }118
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);119
+}120
+121
+json_ptr_t json_at(json_ptr_t json_ptr, int idx) {122
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];123
+ if (token_root.type != JSMN_ARRAY) {124
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);125
+ }126
+ if (idx >= token_root.size) {127
+ return json_err(json_ptr, JSON_ERR_INVALID_SIZE);128
+ }129
+ jsmntok_t token;130
+ int token_idx = 0;131
+ while (json_ptr.ptr < json_ptr.len) {132
+ token = json_ptr.tokens[++json_ptr.ptr];133
+ if (token_idx == idx) {134
+ return json_ptr;135
+ }136
+ if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) {137
+ json_seek(&json_ptr);138
+ if (json_ptr.err) {139
+ return json_ptr;140
+ }141
+ }142
+ token_idx++;143
+ }144
+ return json_err(json_ptr, JSON_ERR_INVALID_MISSING);145
+}146
+147
+json_ptr_t json_query(json_ptr_t json_ptr, const char *query) {148
+ jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr];149
+ if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) {150
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE);151
+ }152
+ jsmntok_t token;153
+ int i = 0;154
+ int query_len = strlen(query);155
+ while (i < query_len) {156
+ token = json_ptr.tokens[json_ptr.ptr];157
+ char c = query[i];158
+ if (c == '.') {159
+ if (token.type != JSMN_OBJECT) {160
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_OBJECT161
+ }162
+ i++;163
+ int j = i;164
+ bool found = false;165
+ for (; j < query_len; j++) {166
+ if (!isalpha(query[j])) {167
+ int key_len = j - i;168
+ if (key_len == 0) {169
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);170
+ }171
+ char key[key_len + 1];172
+ memcpy(key, query + i, key_len);173
+ key[key_len] = '\0';174
+ json_ptr = json_get(json_ptr, key);175
+ i += key_len;176
+ found = true;177
+ break;178
+ }179
+ }180
+ if (!found) {181
+ int key_len = j - i;182
+ if (key_len == 0) {183
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);184
+ }185
+ char key[key_len + 1];186
+ memcpy(key, query + i, key_len);187
+ key[key_len] = '\0';188
+ json_ptr = json_get(json_ptr, key);189
+ i += key_len;190
+ }191
+ continue;192
+ }193
+ if (c == '[') {194
+ if (token.type != JSMN_ARRAY) {195
+ return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_ARRAY196
+ }197
+ i++;198
+ for (int j = i; j < query_len; j++) {199
+ if (query[j] == ']') {200
+ int idx_len = j - i;201
+ if (idx_len == 0) {202
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT);203
+ }204
+ char idx[idx_len + 1];205
+ memcpy(idx, query + i, idx_len);206
+ idx[idx_len] = '\0';207
+ int index = strtoimax(idx, NULL, 10);208
+ json_ptr = json_at(json_ptr, index);209
+ i += idx_len + 1;210
+ break;211
+ }212
+ }213
+ continue;214
+ }215
+ if (!isalpha(c)) {216
+ return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); // invalid char217
+ }218
+ i++;219
+ }220
+ return json_ptr;221
+}222
+223
+#endif /* LIB_JSON_IMPL */224
+#endif /* LIB_JSON_H */