QUOTE: Life is tough, but so are you.

libjson

A simple JSON parser

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 */