C-based memory friendly JSON parser based on Serge Zaitsev's JSMN (https://bitbucket.org/zserge/jsmn/wiki/Home)

Dependents:   _library_jsmn _library_jsmn _library_jsmn

JSMN

jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be easily integrated into resource-limited or embedded projects.

You can find more information about JSON format at json.org

Library sources are available at https://bitbucket.org/zserge/jsmn

The web page with some information about jsmn can be found at http://zserge.com/jsmn.html

Committer:
yoonghm
Date:
Tue May 06 09:33:30 2014 +0000
Revision:
0:46575249ef23
Child:
1:70061827a9c8
Initial version.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
yoonghm 0:46575249ef23 1 /* C-based low-memory footprint JSON parser for mbed
yoonghm 0:46575249ef23 2 * Based on Serge Zaitsev's JSMN https://bitbucket.org/zserge/jsmn/wiki/Home
yoonghm 0:46575249ef23 3 * JSMN is distributed under MIT license.
yoonghm 0:46575249ef23 4 *
yoonghm 0:46575249ef23 5 * Copyright (c) 2014 YoongHM
yoonghm 0:46575249ef23 6 *
yoonghm 0:46575249ef23 7 * Permission is hereby granted, free of charge, to any person obtaining a copy
yoonghm 0:46575249ef23 8 * of this software and associated documentation files (the "Software"), to deal
yoonghm 0:46575249ef23 9 * in the Software without restriction, including without limitation the rights
yoonghm 0:46575249ef23 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
yoonghm 0:46575249ef23 11 * copies of the Software, and to permit persons to whom the Software is
yoonghm 0:46575249ef23 12 * furnished to do so, subject to the following conditions:
yoonghm 0:46575249ef23 13 *
yoonghm 0:46575249ef23 14 * The above copyright notice and this permission notice shall be included in
yoonghm 0:46575249ef23 15 * all copies or substantial portions of the Software.
yoonghm 0:46575249ef23 16 *
yoonghm 0:46575249ef23 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
yoonghm 0:46575249ef23 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
yoonghm 0:46575249ef23 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
yoonghm 0:46575249ef23 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
yoonghm 0:46575249ef23 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
yoonghm 0:46575249ef23 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
yoonghm 0:46575249ef23 23 * THE SOFTWARE.
yoonghm 0:46575249ef23 24 */
yoonghm 0:46575249ef23 25
yoonghm 0:46575249ef23 26 #include <stdlib.h>
yoonghm 0:46575249ef23 27
yoonghm 0:46575249ef23 28 #include "jsmn.h"
yoonghm 0:46575249ef23 29
yoonghm 0:46575249ef23 30
yoonghm 0:46575249ef23 31 /**
yoonghm 0:46575249ef23 32 * Allocates a fresh unused token from the token pull.
yoonghm 0:46575249ef23 33 */
yoonghm 0:46575249ef23 34 static jsmntok_t *
yoonghm 0:46575249ef23 35 jsmn_alloc_token
yoonghm 0:46575249ef23 36 (jsmn_parser *parser
yoonghm 0:46575249ef23 37 ,jsmntok_t *tokens
yoonghm 0:46575249ef23 38 ,size_t num_tokens
yoonghm 0:46575249ef23 39 )
yoonghm 0:46575249ef23 40 {
yoonghm 0:46575249ef23 41 jsmntok_t *tok;
yoonghm 0:46575249ef23 42 if (parser->toknext >= num_tokens)
yoonghm 0:46575249ef23 43 {
yoonghm 0:46575249ef23 44 return NULL;
yoonghm 0:46575249ef23 45 }
yoonghm 0:46575249ef23 46 tok = &tokens[parser->toknext++];
yoonghm 0:46575249ef23 47 tok->start = tok->end = -1;
yoonghm 0:46575249ef23 48 tok->size = 0;
yoonghm 0:46575249ef23 49 tok->parent = -1;
yoonghm 0:46575249ef23 50 return tok;
yoonghm 0:46575249ef23 51 }
yoonghm 0:46575249ef23 52
yoonghm 0:46575249ef23 53
yoonghm 0:46575249ef23 54 /**
yoonghm 0:46575249ef23 55 * Fills token type and boundaries.
yoonghm 0:46575249ef23 56 */
yoonghm 0:46575249ef23 57 static void
yoonghm 0:46575249ef23 58 jsmn_fill_token
yoonghm 0:46575249ef23 59 (jsmntok_t *token
yoonghm 0:46575249ef23 60 ,jsmntype_t type
yoonghm 0:46575249ef23 61 ,int start
yoonghm 0:46575249ef23 62 ,int end
yoonghm 0:46575249ef23 63 )
yoonghm 0:46575249ef23 64 {
yoonghm 0:46575249ef23 65 token->type = type;
yoonghm 0:46575249ef23 66 token->start = start;
yoonghm 0:46575249ef23 67 token->end = end;
yoonghm 0:46575249ef23 68 token->size = 0;
yoonghm 0:46575249ef23 69 }
yoonghm 0:46575249ef23 70
yoonghm 0:46575249ef23 71
yoonghm 0:46575249ef23 72 /**
yoonghm 0:46575249ef23 73 * Fills next available token with JSON primitive.
yoonghm 0:46575249ef23 74 */
yoonghm 0:46575249ef23 75 static int
yoonghm 0:46575249ef23 76 jsmn_parse_primitive
yoonghm 0:46575249ef23 77 (jsmn_parser *parser
yoonghm 0:46575249ef23 78 ,const char *js
yoonghm 0:46575249ef23 79 ,size_t len
yoonghm 0:46575249ef23 80 ,jsmntok_t *tokens
yoonghm 0:46575249ef23 81 ,size_t num_tokens
yoonghm 0:46575249ef23 82 )
yoonghm 0:46575249ef23 83 {
yoonghm 0:46575249ef23 84 jsmntok_t *token;
yoonghm 0:46575249ef23 85 int start;
yoonghm 0:46575249ef23 86
yoonghm 0:46575249ef23 87 start = parser->pos;
yoonghm 0:46575249ef23 88
yoonghm 0:46575249ef23 89 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
yoonghm 0:46575249ef23 90 {
yoonghm 0:46575249ef23 91 switch (js[parser->pos])
yoonghm 0:46575249ef23 92 {
yoonghm 0:46575249ef23 93 case '\t':
yoonghm 0:46575249ef23 94 case '\r':
yoonghm 0:46575249ef23 95 case '\n':
yoonghm 0:46575249ef23 96 case ' ' :
yoonghm 0:46575249ef23 97 case ',' :
yoonghm 0:46575249ef23 98 case ']' :
yoonghm 0:46575249ef23 99 case '}' :
yoonghm 0:46575249ef23 100 goto found;
yoonghm 0:46575249ef23 101 }
yoonghm 0:46575249ef23 102 if (js[parser->pos] < 32 || js[parser->pos] >= 127)
yoonghm 0:46575249ef23 103 {
yoonghm 0:46575249ef23 104 parser->pos = start;
yoonghm 0:46575249ef23 105 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 106 }
yoonghm 0:46575249ef23 107 }
yoonghm 0:46575249ef23 108 parser->pos = start;
yoonghm 0:46575249ef23 109 return JSMN_ERR_PART;
yoonghm 0:46575249ef23 110
yoonghm 0:46575249ef23 111 found:
yoonghm 0:46575249ef23 112 if (tokens == NULL)
yoonghm 0:46575249ef23 113 {
yoonghm 0:46575249ef23 114 parser->pos--;
yoonghm 0:46575249ef23 115 return 0;
yoonghm 0:46575249ef23 116 }
yoonghm 0:46575249ef23 117
yoonghm 0:46575249ef23 118 token = jsmn_alloc_token(parser, tokens, num_tokens);
yoonghm 0:46575249ef23 119 if (token == NULL)
yoonghm 0:46575249ef23 120 {
yoonghm 0:46575249ef23 121 parser->pos = start;
yoonghm 0:46575249ef23 122 return JSMN_ERR_NOMEM;
yoonghm 0:46575249ef23 123 }
yoonghm 0:46575249ef23 124 jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
yoonghm 0:46575249ef23 125
yoonghm 0:46575249ef23 126 token->parent = parser->toksuper;
yoonghm 0:46575249ef23 127 parser->pos--;
yoonghm 0:46575249ef23 128
yoonghm 0:46575249ef23 129 return 0;
yoonghm 0:46575249ef23 130 }
yoonghm 0:46575249ef23 131
yoonghm 0:46575249ef23 132
yoonghm 0:46575249ef23 133 /**
yoonghm 0:46575249ef23 134 * Fill next token with JSON string.
yoonghm 0:46575249ef23 135 */
yoonghm 0:46575249ef23 136 static int
yoonghm 0:46575249ef23 137 jsmn_parse_string
yoonghm 0:46575249ef23 138 (jsmn_parser *parser
yoonghm 0:46575249ef23 139 ,const char *js
yoonghm 0:46575249ef23 140 ,size_t len
yoonghm 0:46575249ef23 141 ,jsmntok_t *tokens
yoonghm 0:46575249ef23 142 ,size_t num_tokens
yoonghm 0:46575249ef23 143 )
yoonghm 0:46575249ef23 144 {
yoonghm 0:46575249ef23 145 jsmntok_t *token;
yoonghm 0:46575249ef23 146 int start = parser->pos;
yoonghm 0:46575249ef23 147
yoonghm 0:46575249ef23 148 parser->pos++;
yoonghm 0:46575249ef23 149
yoonghm 0:46575249ef23 150 /* Skip starting quote */
yoonghm 0:46575249ef23 151 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
yoonghm 0:46575249ef23 152 {
yoonghm 0:46575249ef23 153 char c = js[parser->pos];
yoonghm 0:46575249ef23 154
yoonghm 0:46575249ef23 155 /* Quote: end of string */
yoonghm 0:46575249ef23 156 if (c == '\"')
yoonghm 0:46575249ef23 157 {
yoonghm 0:46575249ef23 158 if (tokens == NULL)
yoonghm 0:46575249ef23 159 return 0;
yoonghm 0:46575249ef23 160
yoonghm 0:46575249ef23 161 token = jsmn_alloc_token(parser, tokens, num_tokens);
yoonghm 0:46575249ef23 162 if (token == NULL)
yoonghm 0:46575249ef23 163 {
yoonghm 0:46575249ef23 164 parser->pos = start;
yoonghm 0:46575249ef23 165 return JSMN_ERR_NOMEM;
yoonghm 0:46575249ef23 166 }
yoonghm 0:46575249ef23 167
yoonghm 0:46575249ef23 168 jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
yoonghm 0:46575249ef23 169 token->parent = parser->toksuper;
yoonghm 0:46575249ef23 170 return 0;
yoonghm 0:46575249ef23 171 }
yoonghm 0:46575249ef23 172
yoonghm 0:46575249ef23 173 /* Backslash: Quoted symbol expected */
yoonghm 0:46575249ef23 174 if (c == '\\')
yoonghm 0:46575249ef23 175 {
yoonghm 0:46575249ef23 176 parser->pos++;
yoonghm 0:46575249ef23 177 switch (js[parser->pos])
yoonghm 0:46575249ef23 178 {
yoonghm 0:46575249ef23 179 /* Allowed escaped symbols */
yoonghm 0:46575249ef23 180 case '\"':
yoonghm 0:46575249ef23 181 case '/' :
yoonghm 0:46575249ef23 182 case '\\':
yoonghm 0:46575249ef23 183 case 'b' :
yoonghm 0:46575249ef23 184 case 'f' :
yoonghm 0:46575249ef23 185 case 'r' :
yoonghm 0:46575249ef23 186 case 'n' :
yoonghm 0:46575249ef23 187 case 't' :
yoonghm 0:46575249ef23 188 break;
yoonghm 0:46575249ef23 189
yoonghm 0:46575249ef23 190 /* Allows escaped symbol \uXXXX */
yoonghm 0:46575249ef23 191 case 'u':
yoonghm 0:46575249ef23 192 parser->pos++;
yoonghm 0:46575249ef23 193 int i = 0;
yoonghm 0:46575249ef23 194 for (; i < 4 && js[parser->pos] != '\0'; i++)
yoonghm 0:46575249ef23 195 {
yoonghm 0:46575249ef23 196 /* If it isn't a hex character we have an ERR */
yoonghm 0:46575249ef23 197 if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
yoonghm 0:46575249ef23 198 (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
yoonghm 0:46575249ef23 199 (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
yoonghm 0:46575249ef23 200 parser->pos = start;
yoonghm 0:46575249ef23 201 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 202 }
yoonghm 0:46575249ef23 203 parser->pos++;
yoonghm 0:46575249ef23 204 }
yoonghm 0:46575249ef23 205 parser->pos--;
yoonghm 0:46575249ef23 206 break;
yoonghm 0:46575249ef23 207
yoonghm 0:46575249ef23 208 /* Unexpected symbol */
yoonghm 0:46575249ef23 209 default:
yoonghm 0:46575249ef23 210 parser->pos = start;
yoonghm 0:46575249ef23 211 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 212 }
yoonghm 0:46575249ef23 213 }
yoonghm 0:46575249ef23 214 }
yoonghm 0:46575249ef23 215 parser->pos = start;
yoonghm 0:46575249ef23 216 return JSMN_ERR_PART;
yoonghm 0:46575249ef23 217 }
yoonghm 0:46575249ef23 218
yoonghm 0:46575249ef23 219
yoonghm 0:46575249ef23 220 /**
yoonghm 0:46575249ef23 221 * Parse JSON string and fill tokens.
yoonghm 0:46575249ef23 222 */
yoonghm 0:46575249ef23 223 int
yoonghm 0:46575249ef23 224 jsmn_parse
yoonghm 0:46575249ef23 225 (jsmn_parser *parser
yoonghm 0:46575249ef23 226 ,const char *js
yoonghm 0:46575249ef23 227 ,size_t len
yoonghm 0:46575249ef23 228 ,jsmntok_t *tokens
yoonghm 0:46575249ef23 229 ,unsigned int num_tokens
yoonghm 0:46575249ef23 230 )
yoonghm 0:46575249ef23 231 {
yoonghm 0:46575249ef23 232 int r;
yoonghm 0:46575249ef23 233 int i;
yoonghm 0:46575249ef23 234 jsmntok_t *token;
yoonghm 0:46575249ef23 235 int count = 0;
yoonghm 0:46575249ef23 236
yoonghm 0:46575249ef23 237 for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++)
yoonghm 0:46575249ef23 238 {
yoonghm 0:46575249ef23 239 char c;
yoonghm 0:46575249ef23 240 jsmntype_t type = JSMN_INVALID;
yoonghm 0:46575249ef23 241
yoonghm 0:46575249ef23 242 c = js[parser->pos];
yoonghm 0:46575249ef23 243 switch (c)
yoonghm 0:46575249ef23 244 {
yoonghm 0:46575249ef23 245 case '{' :
yoonghm 0:46575249ef23 246 case '[' :
yoonghm 0:46575249ef23 247 count++;
yoonghm 0:46575249ef23 248 if (tokens == NULL)
yoonghm 0:46575249ef23 249 break;
yoonghm 0:46575249ef23 250
yoonghm 0:46575249ef23 251 token = jsmn_alloc_token(parser, tokens, num_tokens);
yoonghm 0:46575249ef23 252
yoonghm 0:46575249ef23 253 if (token == NULL)
yoonghm 0:46575249ef23 254 return JSMN_ERR_NOMEM;
yoonghm 0:46575249ef23 255
yoonghm 0:46575249ef23 256 if (parser->toksuper != -1)
yoonghm 0:46575249ef23 257 {
yoonghm 0:46575249ef23 258 tokens[parser->toksuper].size++;
yoonghm 0:46575249ef23 259 token->parent = parser->toksuper;
yoonghm 0:46575249ef23 260 }
yoonghm 0:46575249ef23 261 token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
yoonghm 0:46575249ef23 262 token->start = parser->pos;
yoonghm 0:46575249ef23 263 parser->toksuper = parser->toknext - 1;
yoonghm 0:46575249ef23 264 break;
yoonghm 0:46575249ef23 265
yoonghm 0:46575249ef23 266 case '}':
yoonghm 0:46575249ef23 267 case ']':
yoonghm 0:46575249ef23 268 if (tokens == NULL)
yoonghm 0:46575249ef23 269 break;
yoonghm 0:46575249ef23 270
yoonghm 0:46575249ef23 271 type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
yoonghm 0:46575249ef23 272
yoonghm 0:46575249ef23 273 if (parser->toknext < 1)
yoonghm 0:46575249ef23 274 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 275
yoonghm 0:46575249ef23 276 token = &tokens[parser->toknext - 1];
yoonghm 0:46575249ef23 277 for (;;)
yoonghm 0:46575249ef23 278 {
yoonghm 0:46575249ef23 279 if (token->start != -1 && token->end == -1)
yoonghm 0:46575249ef23 280 {
yoonghm 0:46575249ef23 281 if (token->type != type)
yoonghm 0:46575249ef23 282 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 283
yoonghm 0:46575249ef23 284 token->end = parser->pos + 1;
yoonghm 0:46575249ef23 285 parser->toksuper = token->parent;
yoonghm 0:46575249ef23 286 break;
yoonghm 0:46575249ef23 287 }
yoonghm 0:46575249ef23 288
yoonghm 0:46575249ef23 289 if (token->parent == -1)
yoonghm 0:46575249ef23 290 break;
yoonghm 0:46575249ef23 291
yoonghm 0:46575249ef23 292 token = &tokens[token->parent];
yoonghm 0:46575249ef23 293 }
yoonghm 0:46575249ef23 294 break;
yoonghm 0:46575249ef23 295
yoonghm 0:46575249ef23 296 case '\"':
yoonghm 0:46575249ef23 297 r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
yoonghm 0:46575249ef23 298 if (r < 0)
yoonghm 0:46575249ef23 299 return r;
yoonghm 0:46575249ef23 300
yoonghm 0:46575249ef23 301 count++;
yoonghm 0:46575249ef23 302 if (parser->toksuper != -1 && tokens != NULL)
yoonghm 0:46575249ef23 303 tokens[parser->toksuper].size++;
yoonghm 0:46575249ef23 304 break;
yoonghm 0:46575249ef23 305
yoonghm 0:46575249ef23 306 case '\t':
yoonghm 0:46575249ef23 307 case '\r':
yoonghm 0:46575249ef23 308 case '\n':
yoonghm 0:46575249ef23 309 case ':' :
yoonghm 0:46575249ef23 310 case ',' :
yoonghm 0:46575249ef23 311 case ' ' :
yoonghm 0:46575249ef23 312 break;
yoonghm 0:46575249ef23 313
yoonghm 0:46575249ef23 314 case '-' :
yoonghm 0:46575249ef23 315 case '0' :
yoonghm 0:46575249ef23 316 case '1' :
yoonghm 0:46575249ef23 317 case '2' :
yoonghm 0:46575249ef23 318 case '3' :
yoonghm 0:46575249ef23 319 case '4' :
yoonghm 0:46575249ef23 320 case '5' :
yoonghm 0:46575249ef23 321 case '6' :
yoonghm 0:46575249ef23 322 case '7' :
yoonghm 0:46575249ef23 323 case '8' :
yoonghm 0:46575249ef23 324 case '9' :
yoonghm 0:46575249ef23 325 case 't' :
yoonghm 0:46575249ef23 326 case 'f' :
yoonghm 0:46575249ef23 327 case 'n' :
yoonghm 0:46575249ef23 328 r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
yoonghm 0:46575249ef23 329 if (r < 0)
yoonghm 0:46575249ef23 330 return r;
yoonghm 0:46575249ef23 331
yoonghm 0:46575249ef23 332 count++;
yoonghm 0:46575249ef23 333
yoonghm 0:46575249ef23 334 if (parser->toksuper != -1 && tokens != NULL)
yoonghm 0:46575249ef23 335 tokens[parser->toksuper].size++;
yoonghm 0:46575249ef23 336 break;
yoonghm 0:46575249ef23 337
yoonghm 0:46575249ef23 338 /* Unexpected char in strict mode */
yoonghm 0:46575249ef23 339 default:
yoonghm 0:46575249ef23 340 return JSMN_ERR_INVAL;
yoonghm 0:46575249ef23 341 }
yoonghm 0:46575249ef23 342 }
yoonghm 0:46575249ef23 343
yoonghm 0:46575249ef23 344 for (i = parser->toknext - 1; i >= 0; i--)
yoonghm 0:46575249ef23 345 {
yoonghm 0:46575249ef23 346 /* Unmatched opened object or array */
yoonghm 0:46575249ef23 347 if (tokens[i].start != -1 && tokens[i].end == -1)
yoonghm 0:46575249ef23 348 {
yoonghm 0:46575249ef23 349 return JSMN_ERR_PART;
yoonghm 0:46575249ef23 350 }
yoonghm 0:46575249ef23 351 }
yoonghm 0:46575249ef23 352
yoonghm 0:46575249ef23 353 return count;
yoonghm 0:46575249ef23 354 }
yoonghm 0:46575249ef23 355
yoonghm 0:46575249ef23 356 /**
yoonghm 0:46575249ef23 357 * Creates a new parser based over a given buffer with an array of tokens
yoonghm 0:46575249ef23 358 * available.
yoonghm 0:46575249ef23 359 */
yoonghm 0:46575249ef23 360 void
yoonghm 0:46575249ef23 361 jsmn_init
yoonghm 0:46575249ef23 362 (jsmn_parser *parser)
yoonghm 0:46575249ef23 363 {
yoonghm 0:46575249ef23 364 parser->pos = 0;
yoonghm 0:46575249ef23 365 parser->toknext = 0;
yoonghm 0:46575249ef23 366 parser->toksuper = -1;
yoonghm 0:46575249ef23 367 }
yoonghm 0:46575249ef23 368
yoonghm 0:46575249ef23 369
yoonghm 0:46575249ef23 370