libjson.h (7044B)
1#ifndef LIB_JSON_H 2#define LIB_JSON_H 3 4#ifndef LIB_JSON_IMPL 5#define JSMN_HEADER 6#endif /* LIB_JSON_IMPL */ 7#include <jsmn.h> 8 9typedef enum { 10 JSON_OK = 0, 11 JSON_ERR_INVALID_TYPE = 1, 12 JSON_ERR_INVALID_SIZE = 2, 13 JSON_ERR_INVALID_FORMAT = 3, 14 JSON_ERR_INVALID_MISSING = 4, 15} json_err_t; 16 17typedef struct { 18 jsmntok_t *tokens; 19 int len; 20 int ptr; 21 const char *str; 22 json_err_t err; 23} json_ptr_t; 24 25char *json_str(json_ptr_t json_ptr); 26int json_size(json_ptr_t json_ptr); 27json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str); 28json_ptr_t json_get(json_ptr_t json_ptr, const char *key); 29json_ptr_t json_at(json_ptr_t json_ptr, int idx); 30json_ptr_t json_query(json_ptr_t json_ptr, const char *query); 31 32#ifdef LIB_JSON_IMPL 33 34#include <stdio.h> 35#include <stdlib.h> 36#include <stdbool.h> 37#include <string.h> 38#include <ctype.h> 39#include <inttypes.h> 40 41#include "libjson.h" 42 43char *json_str(json_ptr_t json_ptr) { 44 if (json_ptr.err != JSON_OK) { 45 return NULL; 46 } 47 if (json_ptr.ptr >= json_ptr.len) { 48 return NULL; 49 } 50 jsmntok_t token = json_ptr.tokens[json_ptr.ptr]; 51 int token_len = token.end - token.start; 52 char *str = malloc(token_len + 1); 53 memcpy(str, json_ptr.str + token.start, token_len); 54 str[token_len] = '\0'; 55 return str; 56} 57 58json_ptr_t json_ptr(jsmntok_t *tokens, int len, const char *str) { 59 return (json_ptr_t) { 60 .tokens = tokens, 61 .len = len, 62 .ptr = 0, 63 .str = str, 64 .err = JSON_OK, 65 }; 66} 67 68json_ptr_t json_err(json_ptr_t json_ptr, json_err_t json_err) { 69 json_ptr.err = json_err; 70 return json_ptr; 71} 72 73int json_size(json_ptr_t json_ptr) { 74 return json_ptr.tokens[json_ptr.ptr].size; 75} 76 77json_ptr_t json_parse(jsmntok_t tokens[], int tokens_len, const char *str) { 78 jsmn_parser parser; 79 jsmn_init(&parser); 80 int len = jsmn_parse(&parser, str, strlen(str), tokens, tokens_len); 81 json_ptr_t ptr = json_ptr(tokens, len, str); 82 if (len < 0) { 83 return json_err(ptr, JSON_ERR_INVALID_FORMAT); 84 } 85 return ptr; 86} 87 88/** 89 Increments the `jsmntok_t` pointer until the child `JSMN_OBJECT` or 90 `JSMN_ARRAY` is traversed and the pointer points to the next entry. 91 If no errors occured, the return code is `0`. 92*/ 93void json_seek(json_ptr_t *json_ptr) { 94 jsmntok_t token_root = json_ptr->tokens[json_ptr->ptr]; 95 if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) { 96 json_ptr->err = JSON_ERR_INVALID_TYPE; 97 return; 98 } 99 jsmntok_t token; 100 for (int i = 0; i < token_root.size; i++) { 101 if (token_root.type == JSMN_OBJECT) { 102 token = json_ptr->tokens[++json_ptr->ptr]; // key 103 } 104 token = json_ptr->tokens[++json_ptr->ptr]; // value 105 if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) { 106 json_seek(json_ptr); 107 if (json_ptr->err) { 108 return; 109 } 110 } 111 } 112} 113 114json_ptr_t json_get(json_ptr_t json_ptr, const char *key) { 115 if (json_ptr.err != JSON_OK) { 116 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 117 } 118 jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr]; 119 if (token_root.type != JSMN_OBJECT) { 120 return json_err(json_ptr, JSON_ERR_INVALID_TYPE); 121 } 122 jsmntok_t token; 123 int token_len; 124 while (json_ptr.ptr < json_ptr.len) { 125 if (json_ptr.ptr + 1 >= json_ptr.len) { 126 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 127 } 128 token = json_ptr.tokens[++json_ptr.ptr]; // key 129 token_len = token.end - token.start; 130 char token_key[token_len + 1]; 131 memcpy(token_key, json_ptr.str + token.start, token_len); 132 token_key[token_len] = '\0'; 133 if (strncmp(json_ptr.str + token.start, key, token_len) == 0) { 134 json_ptr.ptr++; 135 return json_ptr; 136 } 137 token = json_ptr.tokens[++json_ptr.ptr]; // value 138 if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) { 139 json_seek(&json_ptr); 140 if (json_ptr.err) { 141 return json_ptr; 142 } 143 } 144 } 145 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 146} 147 148json_ptr_t json_at(json_ptr_t json_ptr, int idx) { 149 if (json_ptr.err != JSON_OK) { 150 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 151 } 152 jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr]; 153 if (token_root.type != JSMN_ARRAY) { 154 return json_err(json_ptr, JSON_ERR_INVALID_TYPE); 155 } 156 if (idx >= token_root.size) { 157 return json_err(json_ptr, JSON_ERR_INVALID_SIZE); 158 } 159 jsmntok_t token; 160 int token_idx = 0; 161 while (json_ptr.ptr < json_ptr.len) { 162 token = json_ptr.tokens[++json_ptr.ptr]; 163 if (token_idx == idx) { 164 return json_ptr; 165 } 166 if (token.type == JSMN_OBJECT || token.type == JSMN_ARRAY) { 167 json_seek(&json_ptr); 168 if (json_ptr.err) { 169 return json_ptr; 170 } 171 } 172 token_idx++; 173 } 174 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 175} 176 177json_ptr_t json_query(json_ptr_t json_ptr, const char *query) { 178 if (json_ptr.err != JSON_OK) { 179 return json_err(json_ptr, JSON_ERR_INVALID_MISSING); 180 } 181 jsmntok_t token_root = json_ptr.tokens[json_ptr.ptr]; 182 if (token_root.type != JSMN_OBJECT && token_root.type != JSMN_ARRAY) { 183 return json_err(json_ptr, JSON_ERR_INVALID_TYPE); 184 } 185 jsmntok_t token; 186 int i = 0; 187 int query_len = strlen(query); 188 while (i < query_len) { 189 token = json_ptr.tokens[json_ptr.ptr]; 190 char c = query[i]; 191 if (c == '.') { 192 if (token.type != JSMN_OBJECT) { 193 return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_OBJECT 194 } 195 i++; 196 int j = i; 197 bool found = false; 198 for (; j < query_len; j++) { 199 if (!isalpha(query[j])) { 200 int key_len = j - i; 201 if (key_len == 0) { 202 return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); 203 } 204 char key[key_len + 1]; 205 memcpy(key, query + i, key_len); 206 key[key_len] = '\0'; 207 json_ptr = json_get(json_ptr, key); 208 i += key_len; 209 found = true; 210 break; 211 } 212 } 213 if (!found) { 214 int key_len = j - i; 215 if (key_len == 0) { 216 return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); 217 } 218 char key[key_len + 1]; 219 memcpy(key, query + i, key_len); 220 key[key_len] = '\0'; 221 json_ptr = json_get(json_ptr, key); 222 i += key_len; 223 } 224 continue; 225 } 226 if (c == '[') { 227 if (token.type != JSMN_ARRAY) { 228 return json_err(json_ptr, JSON_ERR_INVALID_TYPE); // expected JSMN_ARRAY 229 } 230 i++; 231 for (int j = i; j < query_len; j++) { 232 if (query[j] == ']') { 233 int idx_len = j - i; 234 if (idx_len == 0) { 235 return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); 236 } 237 char idx[idx_len + 1]; 238 memcpy(idx, query + i, idx_len); 239 idx[idx_len] = '\0'; 240 int index = strtoimax(idx, NULL, 10); 241 json_ptr = json_at(json_ptr, index); 242 i += idx_len + 1; 243 break; 244 } 245 } 246 continue; 247 } 248 if (!isalpha(c)) { 249 return json_err(json_ptr, JSON_ERR_INVALID_FORMAT); // invalid char 250 } 251 i++; 252 } 253 return json_ptr; 254} 255 256#endif /* LIB_JSON_IMPL */ 257#endif /* LIB_JSON_H */