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
Diff: jsmn.cpp
- Revision:
- 0:46575249ef23
- Child:
- 1:70061827a9c8
diff -r 000000000000 -r 46575249ef23 jsmn.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jsmn.cpp Tue May 06 09:33:30 2014 +0000 @@ -0,0 +1,370 @@ +/* C-based low-memory footprint JSON parser for mbed + * Based on Serge Zaitsev's JSMN https://bitbucket.org/zserge/jsmn/wiki/Home + * JSMN is distributed under MIT license. + * + * Copyright (c) 2014 YoongHM + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <stdlib.h> + +#include "jsmn.h" + + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t * +jsmn_alloc_token +(jsmn_parser *parser +,jsmntok_t *tokens +,size_t num_tokens +) +{ + jsmntok_t *tok; + if (parser->toknext >= num_tokens) + { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; + tok->parent = -1; + return tok; +} + + +/** + * Fills token type and boundaries. + */ +static void +jsmn_fill_token +(jsmntok_t *token +,jsmntype_t type +,int start +,int end +) +{ + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + + +/** + * Fills next available token with JSON primitive. + */ +static int +jsmn_parse_primitive +(jsmn_parser *parser +,const char *js +,size_t len +,jsmntok_t *tokens +,size_t num_tokens +) +{ + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + switch (js[parser->pos]) + { + case '\t': + case '\r': + case '\n': + case ' ' : + case ',' : + case ']' : + case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) + { + parser->pos = start; + return JSMN_ERR_INVAL; + } + } + parser->pos = start; + return JSMN_ERR_PART; + +found: + if (tokens == NULL) + { + parser->pos--; + return 0; + } + + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + { + parser->pos = start; + return JSMN_ERR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); + + token->parent = parser->toksuper; + parser->pos--; + + return 0; +} + + +/** + * Fill next token with JSON string. + */ +static int +jsmn_parse_string +(jsmn_parser *parser +,const char *js +,size_t len +,jsmntok_t *tokens +,size_t num_tokens +) +{ + jsmntok_t *token; + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') + { + if (tokens == NULL) + return 0; + + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + { + parser->pos = start; + return JSMN_ERR_NOMEM; + } + + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); + token->parent = parser->toksuper; + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\') + { + parser->pos++; + switch (js[parser->pos]) + { + /* Allowed escaped symbols */ + case '\"': + case '/' : + case '\\': + case 'b' : + case 'f' : + case 'r' : + case 'n' : + case 't' : + break; + + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + int i = 0; + for (; i < 4 && js[parser->pos] != '\0'; i++) + { + /* If it isn't a hex character we have an ERR */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERR_PART; +} + + +/** + * Parse JSON string and fill tokens. + */ +int +jsmn_parse +(jsmn_parser *parser +,const char *js +,size_t len +,jsmntok_t *tokens +,unsigned int num_tokens +) +{ + int r; + int i; + jsmntok_t *token; + int count = 0; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) + { + char c; + jsmntype_t type = JSMN_INVALID; + + c = js[parser->pos]; + switch (c) + { + case '{' : + case '[' : + count++; + if (tokens == NULL) + break; + + token = jsmn_alloc_token(parser, tokens, num_tokens); + + if (token == NULL) + return JSMN_ERR_NOMEM; + + if (parser->toksuper != -1) + { + tokens[parser->toksuper].size++; + token->parent = parser->toksuper; + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + + case '}': + case ']': + if (tokens == NULL) + break; + + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); + + if (parser->toknext < 1) + return JSMN_ERR_INVAL; + + token = &tokens[parser->toknext - 1]; + for (;;) + { + if (token->start != -1 && token->end == -1) + { + if (token->type != type) + return JSMN_ERR_INVAL; + + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + + if (token->parent == -1) + break; + + token = &tokens[token->parent]; + } + break; + + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) + return r; + + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + + case '\t': + case '\r': + case '\n': + case ':' : + case ',' : + case ' ' : + break; + + case '-' : + case '0' : + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + case 't' : + case 'f' : + case 'n' : + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) + return r; + + count++; + + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + + /* Unexpected char in strict mode */ + default: + return JSMN_ERR_INVAL; + } + } + + for (i = parser->toknext - 1; i >= 0; i--) + { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) + { + return JSMN_ERR_PART; + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void +jsmn_init +(jsmn_parser *parser) +{ + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + + +