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
Revision 0:46575249ef23, committed 2014-05-06
- Comitter:
- yoonghm
- Date:
- Tue May 06 09:33:30 2014 +0000
- Child:
- 1:70061827a9c8
- Commit message:
- Initial version.
Changed in this revision
jsmn.cpp | Show annotated file Show diff for this revision Revisions of this file |
jsmn.h | Show annotated file Show diff for this revision Revisions of this file |
--- /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; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jsmn.h Tue May 06 09:33:30 2014 +0000 @@ -0,0 +1,209 @@ +/* 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. + */ + +#ifndef MBED_JSMN_H +#define MBED_JSMN_H + +/** + * A c-based low-memory low-memory footprint JSON parser for mbed. + * + * The code is based on Serge Zaitsev's JSMN on + * https://bitbucket.org/zserge/jsmn/wiki/Home + * + * @code + *#include "mbed.h" + * + *#include "jsmn.h" + * + *#define MAXTOKEN 64 + * + *const char *jsmn_type_str[] = { + * "PRIMITIVE", + * "OBJECT", + * "ARRAY", + * "STRING" + *}; + * + *int main() + *{ + * const char *js; // Pointer to json string + * int r; // Number of token parsed + * jsmn_parser p; // jsmn parser + * jsmntok_t t[MAXTOKEN]; // Parsed token + * + * // JSON may contain multibyte characters or encoded unicode in + * // \uXXXX format. + * // mbed compiler may complain "invalid multibyte characters ...". + * js = + *"{" + *" \"menu\":" + *" {" + *" \"id\": 1234," + *" \"group\": \"File\"," + *" \"popup\":" + *" {" + *" \"menuitem\":" + *" [" + *" {\"value\": true, \"onclick\" : \"বিশাল\"}," + *" {\"value\": false, 0x1328 : \"groß\"}," + *" {\"value\": null, \"15\u00f8C\": \"3\u0111\"}," + *" {\"value\": \"測試\", -12.34 : 99}" + *" ]" + *" }" + *" }" + *"}"; + * + * jsmn_init(&p); + * r = jsmn_parse(&p, js, strlen(js), t, MAXTOKEN); + * + * printf("Parsed %d tokens\n", r); + * + * printf(" TYPE START END SIZE PAR\n"); + * printf(" ---------- ----- ---- ---- ---\n"); + * + * char ch; + * jsmntok_t at; // A token for general use + * for (int i = 0; i < r; i++) + * { + * at = t[i]; + * printf("Token %2d = %-10.10s (%4d - %4d, %3d, %2d) --> ", + * i, jsmn_type_str[at.type], + * at.start, at.end, + * at.size, at.parent); + * + * switch (at.type) + * { + * case JSMN_STRING: + * printf("%-10.*s\n", at.end - at.start + 2, js + at.start - 1); + * break; + * + * case JSMN_PRIMITIVE: + * ch = *(js + at.start); + * + * if (isdigit(ch) || ch == '-') + * printf("%-10.*s\n", at.end - at.start, js + at.start); + * else if (tolower(ch) == 'n') + * printf("null\n"); + * else if (tolower(ch) == 't') + * printf("true\n"); + * else if (tolower(ch) == 'f') + * printf("false\n"); + * break; + * + * default: + * printf("\n"); + * break; + * } + * } + * + * while (1) + * ; + *} + * @endcode + */ + +/** jsmntype_t - JSON type recongized by JSMN. + * There are + * - Primitive: number, boolean (true/false) or null + * - Object + * - Array + * - String + */ +typedef enum { + JSMN_PRIMITIVE = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_INVALID = 4 +} jsmntype_t; + + +/** Error message return by JSMN API + * + */ +#define JSMN_ERR_OKAY 0 /* No problem */ +#define JSMN_ERR_NOMEM -1 /* Not enough tokens were provided */ +#define JSMN_ERR_INVAL -2 /* Invalid character inside JSON string */ +#define JSMN_ERR_PART -3 /* Incomplete JSON packet */ + + +/** jsmntok_t - JSON token structure. + * + * It is a c structure that contained parsed JSON token. + * + * @param type type (primitive, object, array, string) + * @param start start position in JSON data string, excl " if is a string + * @param end end position in JSON data string, incl " if is a string + * @param size number of token directly under it + * @param parent link to its direct parent token number + */ +typedef struct { + jsmntype_t type; + int start; /* Token's start pos;excl " if is a str */ + int end; /* Token's last pos;incl " if is a str */ + int size; /* Number of token directly under it */ + int parent; /* Nth-token which is its direct parent */ +} jsmntok_t; + + +/** JSON parser - JSON parser structure. + * + * Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, (parent or array)*/ +} jsmn_parser; + + +/** Initialize jsmn_parser structure for a new JSON parsing + * + * @param *parser jsmn_parser structure + */ +void +jsmn_init +(jsmn_parser *parser +); + +/** Parse a given JSON data string into array of jsmntok_t tokens. + * + * @param *parser jsmn_parser structure + * @param *js JSON data string + * @param len string length of *js + * @param *tokens tokens to hold the parsed data + */ +int +jsmn_parse +(jsmn_parser *parser +,const char *js +,size_t len +,jsmntok_t *tokens +,unsigned int num_tokens +); + + +#endif // JSMN_H \ No newline at end of file