JSON library based on JSMN lib

Dependents:   ATT_WNCInterface_Info WNCInterface_HTTP_example NerfUS-Coord Mbed_Prototype_copy_4_INNO_day_15_6_2017 ... more

C++ JSON wrapper over JSMN lib (https://github.com/zserge/jsmn).

This C++ Class is a set of common tools/procedures as a C++ wrapper over JSMN JSON parser library. It is intended to provide the boiler-plate code, with intentions to reduce code clutter, in more of C++ fashion.

In contrast to original library, Json is intended to work strictly with valid JSON structures. Non-standard JSON structures should result in an error.

This class works explicitly on the indices returned by underlying JSMN library. In the scope of this class, its function parameters, return types, and documentation, the term 'index' will always mean the index of JSMN tokens, parsed by the Json constructor, unless and until explicitly mentioned otherwise.

Committer:
mercurywaters
Date:
Fri May 13 06:07:34 2016 +0000
Revision:
0:7f4a18b3fd82
Child:
3:fab591fca1e7
First cut code commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
mercurywaters 0:7f4a18b3fd82 1 #include "jsmn.h"
mercurywaters 0:7f4a18b3fd82 2
mercurywaters 0:7f4a18b3fd82 3 /**
mercurywaters 0:7f4a18b3fd82 4 * Allocates a fresh unused token from the token pull.
mercurywaters 0:7f4a18b3fd82 5 */
mercurywaters 0:7f4a18b3fd82 6 static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser,
mercurywaters 0:7f4a18b3fd82 7 jsmntok_t *tokens, size_t num_tokens)
mercurywaters 0:7f4a18b3fd82 8 {
mercurywaters 0:7f4a18b3fd82 9 jsmntok_t *tok;
mercurywaters 0:7f4a18b3fd82 10 if (parser->toknext >= num_tokens) {
mercurywaters 0:7f4a18b3fd82 11 return NULL;
mercurywaters 0:7f4a18b3fd82 12 }
mercurywaters 0:7f4a18b3fd82 13 tok = &tokens[parser->toknext++];
mercurywaters 0:7f4a18b3fd82 14 tok->start = tok->end = -1;
mercurywaters 0:7f4a18b3fd82 15 tok->childCount = 0;
mercurywaters 0:7f4a18b3fd82 16 tok->parent = -1;
mercurywaters 0:7f4a18b3fd82 17 return tok;
mercurywaters 0:7f4a18b3fd82 18 }
mercurywaters 0:7f4a18b3fd82 19
mercurywaters 0:7f4a18b3fd82 20 /**
mercurywaters 0:7f4a18b3fd82 21 * Fills token type and boundaries.
mercurywaters 0:7f4a18b3fd82 22 */
mercurywaters 0:7f4a18b3fd82 23 static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
mercurywaters 0:7f4a18b3fd82 24 int start, int end)
mercurywaters 0:7f4a18b3fd82 25 {
mercurywaters 0:7f4a18b3fd82 26 token->type = type;
mercurywaters 0:7f4a18b3fd82 27 token->start = start;
mercurywaters 0:7f4a18b3fd82 28 token->end = end;
mercurywaters 0:7f4a18b3fd82 29 token->childCount = 0;
mercurywaters 0:7f4a18b3fd82 30 }
mercurywaters 0:7f4a18b3fd82 31
mercurywaters 0:7f4a18b3fd82 32 /**
mercurywaters 0:7f4a18b3fd82 33 * Fills next available token with JSON primitive.
mercurywaters 0:7f4a18b3fd82 34 */
mercurywaters 0:7f4a18b3fd82 35 static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
mercurywaters 0:7f4a18b3fd82 36 size_t len, jsmntok_t *tokens, size_t num_tokens)
mercurywaters 0:7f4a18b3fd82 37 {
mercurywaters 0:7f4a18b3fd82 38 jsmntok_t *token;
mercurywaters 0:7f4a18b3fd82 39 int start;
mercurywaters 0:7f4a18b3fd82 40
mercurywaters 0:7f4a18b3fd82 41 start = parser->pos;
mercurywaters 0:7f4a18b3fd82 42
mercurywaters 0:7f4a18b3fd82 43 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
mercurywaters 0:7f4a18b3fd82 44 switch (js[parser->pos]) {
mercurywaters 0:7f4a18b3fd82 45 case '\t' :
mercurywaters 0:7f4a18b3fd82 46 case '\r' :
mercurywaters 0:7f4a18b3fd82 47 case '\n' :
mercurywaters 0:7f4a18b3fd82 48 case ' ' :
mercurywaters 0:7f4a18b3fd82 49 case ',' :
mercurywaters 0:7f4a18b3fd82 50 case ']' :
mercurywaters 0:7f4a18b3fd82 51 case '}' :
mercurywaters 0:7f4a18b3fd82 52 goto found;
mercurywaters 0:7f4a18b3fd82 53 }
mercurywaters 0:7f4a18b3fd82 54 if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
mercurywaters 0:7f4a18b3fd82 55 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 56 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 57 }
mercurywaters 0:7f4a18b3fd82 58 }
mercurywaters 0:7f4a18b3fd82 59 /* primitive must be followed by a comma/object/array */
mercurywaters 0:7f4a18b3fd82 60 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 61 return JSMN_ERROR_PART;
mercurywaters 0:7f4a18b3fd82 62
mercurywaters 0:7f4a18b3fd82 63 found:
mercurywaters 0:7f4a18b3fd82 64 if (tokens == NULL) {
mercurywaters 0:7f4a18b3fd82 65 parser->pos--;
mercurywaters 0:7f4a18b3fd82 66 return 0;
mercurywaters 0:7f4a18b3fd82 67 }
mercurywaters 0:7f4a18b3fd82 68 token = jsmn_alloc_token(parser, tokens, num_tokens);
mercurywaters 0:7f4a18b3fd82 69 if (token == NULL) {
mercurywaters 0:7f4a18b3fd82 70 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 71 return JSMN_ERROR_NOMEM;
mercurywaters 0:7f4a18b3fd82 72 }
mercurywaters 0:7f4a18b3fd82 73 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
mercurywaters 0:7f4a18b3fd82 74 token->parent = parser->toksuper;
mercurywaters 0:7f4a18b3fd82 75 parser->pos--;
mercurywaters 0:7f4a18b3fd82 76 return 0;
mercurywaters 0:7f4a18b3fd82 77 }
mercurywaters 0:7f4a18b3fd82 78
mercurywaters 0:7f4a18b3fd82 79 /**
mercurywaters 0:7f4a18b3fd82 80 * Fills next token with JSON string.
mercurywaters 0:7f4a18b3fd82 81 */
mercurywaters 0:7f4a18b3fd82 82 static int jsmn_parse_string(jsmn_parser *parser, const char *js,
mercurywaters 0:7f4a18b3fd82 83 size_t len, jsmntok_t *tokens, size_t num_tokens, unsigned char isKey )
mercurywaters 0:7f4a18b3fd82 84 {
mercurywaters 0:7f4a18b3fd82 85 jsmntok_t *token;
mercurywaters 0:7f4a18b3fd82 86
mercurywaters 0:7f4a18b3fd82 87 int start = parser->pos;
mercurywaters 0:7f4a18b3fd82 88
mercurywaters 0:7f4a18b3fd82 89 parser->pos++;
mercurywaters 0:7f4a18b3fd82 90
mercurywaters 0:7f4a18b3fd82 91 /* Skip starting quote */
mercurywaters 0:7f4a18b3fd82 92 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
mercurywaters 0:7f4a18b3fd82 93 char c = js[parser->pos];
mercurywaters 0:7f4a18b3fd82 94
mercurywaters 0:7f4a18b3fd82 95 /* Quote: end of string */
mercurywaters 0:7f4a18b3fd82 96 if (c == '\"') {
mercurywaters 0:7f4a18b3fd82 97 if (tokens == NULL) {
mercurywaters 0:7f4a18b3fd82 98 return 0;
mercurywaters 0:7f4a18b3fd82 99 }
mercurywaters 0:7f4a18b3fd82 100 token = jsmn_alloc_token(parser, tokens, num_tokens);
mercurywaters 0:7f4a18b3fd82 101 if (token == NULL) {
mercurywaters 0:7f4a18b3fd82 102 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 103 return JSMN_ERROR_NOMEM;
mercurywaters 0:7f4a18b3fd82 104 }
mercurywaters 0:7f4a18b3fd82 105 if ( isKey == 1 ) {
mercurywaters 0:7f4a18b3fd82 106 jsmn_fill_token(token, JSMN_KEY, start+1, parser->pos);
mercurywaters 0:7f4a18b3fd82 107 }
mercurywaters 0:7f4a18b3fd82 108 else {
mercurywaters 0:7f4a18b3fd82 109 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
mercurywaters 0:7f4a18b3fd82 110 }
mercurywaters 0:7f4a18b3fd82 111 token->parent = parser->toksuper;
mercurywaters 0:7f4a18b3fd82 112 return 0;
mercurywaters 0:7f4a18b3fd82 113 }
mercurywaters 0:7f4a18b3fd82 114
mercurywaters 0:7f4a18b3fd82 115 /* Backslash: Quoted symbol expected */
mercurywaters 0:7f4a18b3fd82 116 if (c == '\\' && parser->pos + 1 < len) {
mercurywaters 0:7f4a18b3fd82 117 int i;
mercurywaters 0:7f4a18b3fd82 118 parser->pos++;
mercurywaters 0:7f4a18b3fd82 119 switch (js[parser->pos]) {
mercurywaters 0:7f4a18b3fd82 120 /* Allowed escaped symbols */
mercurywaters 0:7f4a18b3fd82 121 case '\"':
mercurywaters 0:7f4a18b3fd82 122 case '/' :
mercurywaters 0:7f4a18b3fd82 123 case '\\' :
mercurywaters 0:7f4a18b3fd82 124 case 'b' :
mercurywaters 0:7f4a18b3fd82 125 case 'f' :
mercurywaters 0:7f4a18b3fd82 126 case 'r' :
mercurywaters 0:7f4a18b3fd82 127 case 'n' :
mercurywaters 0:7f4a18b3fd82 128 case 't' :
mercurywaters 0:7f4a18b3fd82 129 break;
mercurywaters 0:7f4a18b3fd82 130 /* Allows escaped symbol \uXXXX */
mercurywaters 0:7f4a18b3fd82 131 case 'u':
mercurywaters 0:7f4a18b3fd82 132 parser->pos++;
mercurywaters 0:7f4a18b3fd82 133 for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
mercurywaters 0:7f4a18b3fd82 134 /* If it isn't a hex character we have an error */
mercurywaters 0:7f4a18b3fd82 135 if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
mercurywaters 0:7f4a18b3fd82 136 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
mercurywaters 0:7f4a18b3fd82 137 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
mercurywaters 0:7f4a18b3fd82 138 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 139 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 140 }
mercurywaters 0:7f4a18b3fd82 141 parser->pos++;
mercurywaters 0:7f4a18b3fd82 142 }
mercurywaters 0:7f4a18b3fd82 143 parser->pos--;
mercurywaters 0:7f4a18b3fd82 144 break;
mercurywaters 0:7f4a18b3fd82 145 /* Unexpected symbol */
mercurywaters 0:7f4a18b3fd82 146 default:
mercurywaters 0:7f4a18b3fd82 147 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 148 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 149 }
mercurywaters 0:7f4a18b3fd82 150 }
mercurywaters 0:7f4a18b3fd82 151 }
mercurywaters 0:7f4a18b3fd82 152 parser->pos = start;
mercurywaters 0:7f4a18b3fd82 153 return JSMN_ERROR_PART;
mercurywaters 0:7f4a18b3fd82 154 }
mercurywaters 0:7f4a18b3fd82 155
mercurywaters 0:7f4a18b3fd82 156 /**
mercurywaters 0:7f4a18b3fd82 157 * Parse JSON string and fill tokens.
mercurywaters 0:7f4a18b3fd82 158 */
mercurywaters 0:7f4a18b3fd82 159 int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
mercurywaters 0:7f4a18b3fd82 160 jsmntok_t *tokens, unsigned int num_tokens)
mercurywaters 0:7f4a18b3fd82 161 {
mercurywaters 0:7f4a18b3fd82 162 int r;
mercurywaters 0:7f4a18b3fd82 163 int i;
mercurywaters 0:7f4a18b3fd82 164 jsmntok_t *token;
mercurywaters 0:7f4a18b3fd82 165 int count = parser->toknext;
mercurywaters 0:7f4a18b3fd82 166
mercurywaters 0:7f4a18b3fd82 167 unsigned char isKey = 1;
mercurywaters 0:7f4a18b3fd82 168
mercurywaters 0:7f4a18b3fd82 169 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
mercurywaters 0:7f4a18b3fd82 170 char c;
mercurywaters 0:7f4a18b3fd82 171 jsmntype_t type;
mercurywaters 0:7f4a18b3fd82 172
mercurywaters 0:7f4a18b3fd82 173 c = js[parser->pos];
mercurywaters 0:7f4a18b3fd82 174 switch (c) {
mercurywaters 0:7f4a18b3fd82 175 case '{':
mercurywaters 0:7f4a18b3fd82 176 case '[':
mercurywaters 0:7f4a18b3fd82 177 count++;
mercurywaters 0:7f4a18b3fd82 178 if (tokens == NULL) {
mercurywaters 0:7f4a18b3fd82 179 break;
mercurywaters 0:7f4a18b3fd82 180 }
mercurywaters 0:7f4a18b3fd82 181 token = jsmn_alloc_token(parser, tokens, num_tokens);
mercurywaters 0:7f4a18b3fd82 182 if (token == NULL)
mercurywaters 0:7f4a18b3fd82 183 return JSMN_ERROR_NOMEM;
mercurywaters 0:7f4a18b3fd82 184 if (parser->toksuper != -1) {
mercurywaters 0:7f4a18b3fd82 185 tokens[parser->toksuper].childCount++;
mercurywaters 0:7f4a18b3fd82 186 token->parent = parser->toksuper;
mercurywaters 0:7f4a18b3fd82 187 }
mercurywaters 0:7f4a18b3fd82 188 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
mercurywaters 0:7f4a18b3fd82 189 token->start = parser->pos;
mercurywaters 0:7f4a18b3fd82 190 parser->toksuper = parser->toknext - 1;
mercurywaters 0:7f4a18b3fd82 191 if ( token -> type == JSMN_OBJECT ) {
mercurywaters 0:7f4a18b3fd82 192 isKey = 1;
mercurywaters 0:7f4a18b3fd82 193 }
mercurywaters 0:7f4a18b3fd82 194 break;
mercurywaters 0:7f4a18b3fd82 195 case '}':
mercurywaters 0:7f4a18b3fd82 196 case ']':
mercurywaters 0:7f4a18b3fd82 197 if (tokens == NULL)
mercurywaters 0:7f4a18b3fd82 198 break;
mercurywaters 0:7f4a18b3fd82 199 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
mercurywaters 0:7f4a18b3fd82 200 if (parser->toknext < 1) {
mercurywaters 0:7f4a18b3fd82 201 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 202 }
mercurywaters 0:7f4a18b3fd82 203 token = &tokens[parser->toknext - 1];
mercurywaters 0:7f4a18b3fd82 204 for (;;) {
mercurywaters 0:7f4a18b3fd82 205 if (token->start != -1 && token->end == -1) {
mercurywaters 0:7f4a18b3fd82 206 if (token->type != type) {
mercurywaters 0:7f4a18b3fd82 207 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 208 }
mercurywaters 0:7f4a18b3fd82 209 token->end = parser->pos + 1;
mercurywaters 0:7f4a18b3fd82 210 parser->toksuper = token->parent;
mercurywaters 0:7f4a18b3fd82 211 break;
mercurywaters 0:7f4a18b3fd82 212 }
mercurywaters 0:7f4a18b3fd82 213 if (token->parent == -1) {
mercurywaters 0:7f4a18b3fd82 214 break;
mercurywaters 0:7f4a18b3fd82 215 }
mercurywaters 0:7f4a18b3fd82 216 token = &tokens[token->parent];
mercurywaters 0:7f4a18b3fd82 217 }
mercurywaters 0:7f4a18b3fd82 218 break;
mercurywaters 0:7f4a18b3fd82 219 case '\"':
mercurywaters 0:7f4a18b3fd82 220 r = jsmn_parse_string(parser, js, len, tokens, num_tokens, isKey);
mercurywaters 0:7f4a18b3fd82 221 if (r < 0) return r;
mercurywaters 0:7f4a18b3fd82 222 count++;
mercurywaters 0:7f4a18b3fd82 223 if (parser->toksuper != -1 && tokens != NULL)
mercurywaters 0:7f4a18b3fd82 224 tokens[parser->toksuper].childCount++;
mercurywaters 0:7f4a18b3fd82 225 break;
mercurywaters 0:7f4a18b3fd82 226 case '\t' :
mercurywaters 0:7f4a18b3fd82 227 case '\r' :
mercurywaters 0:7f4a18b3fd82 228 case '\n' :
mercurywaters 0:7f4a18b3fd82 229 case ' ':
mercurywaters 0:7f4a18b3fd82 230 break;
mercurywaters 0:7f4a18b3fd82 231 case ':':
mercurywaters 0:7f4a18b3fd82 232 isKey = 0;
mercurywaters 0:7f4a18b3fd82 233 parser->toksuper = parser->toknext - 1;
mercurywaters 0:7f4a18b3fd82 234 break;
mercurywaters 0:7f4a18b3fd82 235 case ',':
mercurywaters 0:7f4a18b3fd82 236 if (tokens != NULL && parser->toksuper != -1 &&
mercurywaters 0:7f4a18b3fd82 237 tokens[parser->toksuper].type != JSMN_ARRAY &&
mercurywaters 0:7f4a18b3fd82 238 tokens[parser->toksuper].type != JSMN_OBJECT) {
mercurywaters 0:7f4a18b3fd82 239 parser->toksuper = tokens[parser->toksuper].parent;
mercurywaters 0:7f4a18b3fd82 240 }
mercurywaters 0:7f4a18b3fd82 241 isKey = 1;
mercurywaters 0:7f4a18b3fd82 242 break;
mercurywaters 0:7f4a18b3fd82 243 /* In strict mode primitives are: numbers and booleans */
mercurywaters 0:7f4a18b3fd82 244 case '-':
mercurywaters 0:7f4a18b3fd82 245 case '0':
mercurywaters 0:7f4a18b3fd82 246 case '1' :
mercurywaters 0:7f4a18b3fd82 247 case '2':
mercurywaters 0:7f4a18b3fd82 248 case '3' :
mercurywaters 0:7f4a18b3fd82 249 case '4':
mercurywaters 0:7f4a18b3fd82 250 case '5':
mercurywaters 0:7f4a18b3fd82 251 case '6':
mercurywaters 0:7f4a18b3fd82 252 case '7' :
mercurywaters 0:7f4a18b3fd82 253 case '8':
mercurywaters 0:7f4a18b3fd82 254 case '9':
mercurywaters 0:7f4a18b3fd82 255 case 't':
mercurywaters 0:7f4a18b3fd82 256 case 'f':
mercurywaters 0:7f4a18b3fd82 257 case 'n' :
mercurywaters 0:7f4a18b3fd82 258 /* And they must not be keys of the object */
mercurywaters 0:7f4a18b3fd82 259 if (tokens != NULL && parser->toksuper != -1) {
mercurywaters 0:7f4a18b3fd82 260 jsmntok_t *t = &tokens[parser->toksuper];
mercurywaters 0:7f4a18b3fd82 261 if (t->type == JSMN_OBJECT ||
mercurywaters 0:7f4a18b3fd82 262 (t->type == JSMN_STRING && t->childCount != 0)) {
mercurywaters 0:7f4a18b3fd82 263 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 264 }
mercurywaters 0:7f4a18b3fd82 265 }
mercurywaters 0:7f4a18b3fd82 266 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
mercurywaters 0:7f4a18b3fd82 267 if (r < 0) return r;
mercurywaters 0:7f4a18b3fd82 268 count++;
mercurywaters 0:7f4a18b3fd82 269 if (parser->toksuper != -1 && tokens != NULL)
mercurywaters 0:7f4a18b3fd82 270 tokens[parser->toksuper].childCount++;
mercurywaters 0:7f4a18b3fd82 271 break;
mercurywaters 0:7f4a18b3fd82 272
mercurywaters 0:7f4a18b3fd82 273 /* Unexpected char in strict mode */
mercurywaters 0:7f4a18b3fd82 274 default:
mercurywaters 0:7f4a18b3fd82 275 return JSMN_ERROR_INVAL;
mercurywaters 0:7f4a18b3fd82 276 }
mercurywaters 0:7f4a18b3fd82 277 }
mercurywaters 0:7f4a18b3fd82 278
mercurywaters 0:7f4a18b3fd82 279 if (tokens != NULL) {
mercurywaters 0:7f4a18b3fd82 280 for (i = parser->toknext - 1; i >= 0; i--) {
mercurywaters 0:7f4a18b3fd82 281 /* Unmatched opened object or array */
mercurywaters 0:7f4a18b3fd82 282 if (tokens[i].start != -1 && tokens[i].end == -1) {
mercurywaters 0:7f4a18b3fd82 283 return JSMN_ERROR_PART;
mercurywaters 0:7f4a18b3fd82 284 }
mercurywaters 0:7f4a18b3fd82 285 }
mercurywaters 0:7f4a18b3fd82 286 }
mercurywaters 0:7f4a18b3fd82 287
mercurywaters 0:7f4a18b3fd82 288 return count;
mercurywaters 0:7f4a18b3fd82 289 }
mercurywaters 0:7f4a18b3fd82 290
mercurywaters 0:7f4a18b3fd82 291 /**
mercurywaters 0:7f4a18b3fd82 292 * Creates a new parser based over a given buffer with an array of tokens
mercurywaters 0:7f4a18b3fd82 293 * available.
mercurywaters 0:7f4a18b3fd82 294 */
mercurywaters 0:7f4a18b3fd82 295 void jsmn_init(jsmn_parser *parser)
mercurywaters 0:7f4a18b3fd82 296 {
mercurywaters 0:7f4a18b3fd82 297 parser->pos = 0;
mercurywaters 0:7f4a18b3fd82 298 parser->toknext = 0;
mercurywaters 0:7f4a18b3fd82 299 parser->toksuper = -1;
mercurywaters 0:7f4a18b3fd82 300 }