mirror of
				https://github.com/eledio-devices/thirdparty-ArduinoJson.git
				synced 2025-10-31 00:32:37 +01:00 
			
		
		
		
	Initial commit
This commit is contained in:
		
							
								
								
									
										69
									
								
								ArduinoJsonParser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								ArduinoJsonParser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * malloc-free JSON parser for Arduino | ||||
|  * Benoit Blanchon 2014 | ||||
|  * MIT License | ||||
|  */ | ||||
|  | ||||
| #ifndef __ARDUINOJSONPARSER_H | ||||
| #define __ARDUINOJSONPARSER_H | ||||
|  | ||||
| #include <Arduino.h> | ||||
| #include "utility/jsmn.h" | ||||
|  | ||||
| template <int N> | ||||
| class ArduinoJsonParser | ||||
| { | ||||
| public: | ||||
|  | ||||
| 	ArduinoJsonParser() | ||||
| 	{ | ||||
| 		jsmn_init(&parser); | ||||
| 	} | ||||
|  | ||||
| 	boolean parse(char* jsonString) | ||||
| 	{ | ||||
| 		buffer = jsonString; | ||||
|  | ||||
| 		int tokenCount = sizeof(tokens) / sizeof(tokens[0]); | ||||
|  | ||||
| 		if (JSMN_SUCCESS != jsmn_parse(&parser, jsonString, tokens, tokenCount)) | ||||
| 			return false; | ||||
|  | ||||
| 		// Add null termination to each token | ||||
| 		for (int i = 0; i < tokenCount; i++) | ||||
| 		{ | ||||
| 			buffer[tokens[i].end] = 0; | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	char* getValue(char* name) | ||||
| 	{ | ||||
| 		// Scan each keys, every two other token | ||||
| 		// (skip index 0, because it's the whole json object) | ||||
| 		for (int i = 1; i < 2 * N; i += 2) | ||||
| 		{ | ||||
| 			// Early break if we reach the last token | ||||
| 			if (i >= parser.toknext) break; | ||||
|  | ||||
| 			// Get key token string | ||||
| 			char* key = buffer + tokens[i].start; | ||||
|  | ||||
| 			// Compare with desired name, ignoring case | ||||
| 			if (strcasecmp(name, key) == 0) | ||||
| 			{ | ||||
| 				return buffer + tokens[i + 1].start; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	char* buffer; | ||||
| 	jsmn_parser parser; | ||||
| 	jsmntok_t tokens[N * 2 + 1]; | ||||
| }; | ||||
|  | ||||
| #endif | ||||
|  | ||||
							
								
								
									
										255
									
								
								utility/jsmn.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								utility/jsmn.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,255 @@ | ||||
| #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; | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 	tok->parent = -1; | ||||
| #endif | ||||
| 	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 jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, | ||||
| 		jsmntok_t *tokens, size_t num_tokens) { | ||||
| 	jsmntok_t *token; | ||||
| 	int start; | ||||
|  | ||||
| 	start = parser->pos; | ||||
|  | ||||
| 	for (; js[parser->pos] != '\0'; parser->pos++) { | ||||
| 		switch (js[parser->pos]) { | ||||
| #ifndef JSMN_STRICT | ||||
| 			/* In strict mode primitive must be followed by "," or "}" or "]" */ | ||||
| 			case ':': | ||||
| #endif | ||||
| 			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_ERROR_INVAL; | ||||
| 		} | ||||
| 	} | ||||
| #ifdef JSMN_STRICT | ||||
| 	/* In strict mode primitive must be followed by a comma/object/array */ | ||||
| 	parser->pos = start; | ||||
| 	return JSMN_ERROR_PART; | ||||
| #endif | ||||
|  | ||||
| found: | ||||
| 	token = jsmn_alloc_token(parser, tokens, num_tokens); | ||||
| 	if (token == NULL) { | ||||
| 		parser->pos = start; | ||||
| 		return JSMN_ERROR_NOMEM; | ||||
| 	} | ||||
| 	jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 	token->parent = parser->toksuper; | ||||
| #endif | ||||
| 	parser->pos--; | ||||
| 	return JSMN_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Filsl next token with JSON string. | ||||
|  */ | ||||
| static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, | ||||
| 		jsmntok_t *tokens, size_t num_tokens) { | ||||
| 	jsmntok_t *token; | ||||
|  | ||||
| 	int start = parser->pos; | ||||
|  | ||||
| 	parser->pos++; | ||||
|  | ||||
| 	/* Skip starting quote */ | ||||
| 	for (; js[parser->pos] != '\0'; parser->pos++) { | ||||
| 		char c = js[parser->pos]; | ||||
|  | ||||
| 		/* Quote: end of string */ | ||||
| 		if (c == '\"') { | ||||
| 			token = jsmn_alloc_token(parser, tokens, num_tokens); | ||||
| 			if (token == NULL) { | ||||
| 				parser->pos = start; | ||||
| 				return JSMN_ERROR_NOMEM; | ||||
| 			} | ||||
| 			jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 			token->parent = parser->toksuper; | ||||
| #endif | ||||
| 			return JSMN_SUCCESS; | ||||
| 		} | ||||
|  | ||||
| 		/* 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': | ||||
| 					/* TODO */ | ||||
| 					break; | ||||
| 				/* Unexpected symbol */ | ||||
| 				default: | ||||
| 					parser->pos = start; | ||||
| 					return JSMN_ERROR_INVAL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	parser->pos = start; | ||||
| 	return JSMN_ERROR_PART; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Parse JSON string and fill tokens. | ||||
|  */ | ||||
| jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, jsmntok_t *tokens,  | ||||
| 		unsigned int num_tokens) { | ||||
| 	jsmnerr_t r; | ||||
| 	int i; | ||||
| 	jsmntok_t *token; | ||||
|  | ||||
| 	for (; js[parser->pos] != '\0'; parser->pos++) { | ||||
| 		char c; | ||||
| 		jsmntype_t type; | ||||
|  | ||||
| 		c = js[parser->pos]; | ||||
| 		switch (c) { | ||||
| 			case '{': case '[': | ||||
| 				token = jsmn_alloc_token(parser, tokens, num_tokens); | ||||
| 				if (token == NULL) | ||||
| 					return JSMN_ERROR_NOMEM; | ||||
| 				if (parser->toksuper != -1) { | ||||
| 					tokens[parser->toksuper].size++; | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 					token->parent = parser->toksuper; | ||||
| #endif | ||||
| 				} | ||||
| 				token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | ||||
| 				token->start = parser->pos; | ||||
| 				parser->toksuper = parser->toknext - 1; | ||||
| 				break; | ||||
| 			case '}': case ']': | ||||
| 				type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 				if (parser->toknext < 1) { | ||||
| 					return JSMN_ERROR_INVAL; | ||||
| 				} | ||||
| 				token = &tokens[parser->toknext - 1]; | ||||
| 				for (;;) { | ||||
| 					if (token->start != -1 && token->end == -1) { | ||||
| 						if (token->type != type) { | ||||
| 							return JSMN_ERROR_INVAL; | ||||
| 						} | ||||
| 						token->end = parser->pos + 1; | ||||
| 						parser->toksuper = token->parent; | ||||
| 						break; | ||||
| 					} | ||||
| 					if (token->parent == -1) { | ||||
| 						break; | ||||
| 					} | ||||
| 					token = &tokens[token->parent]; | ||||
| 				} | ||||
| #else | ||||
| 				for (i = parser->toknext - 1; i >= 0; i--) { | ||||
| 					token = &tokens[i]; | ||||
| 					if (token->start != -1 && token->end == -1) { | ||||
| 						if (token->type != type) { | ||||
| 							return JSMN_ERROR_INVAL; | ||||
| 						} | ||||
| 						parser->toksuper = -1; | ||||
| 						token->end = parser->pos + 1; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				/* Error if unmatched closing bracket */ | ||||
| 				if (i == -1) return JSMN_ERROR_INVAL; | ||||
| 				for (; i >= 0; i--) { | ||||
| 					token = &tokens[i]; | ||||
| 					if (token->start != -1 && token->end == -1) { | ||||
| 						parser->toksuper = i; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| #endif | ||||
| 				break; | ||||
| 			case '\"': | ||||
| 				r = jsmn_parse_string(parser, js, tokens, num_tokens); | ||||
| 				if (r < 0) return r; | ||||
| 				if (parser->toksuper != -1) | ||||
| 					tokens[parser->toksuper].size++; | ||||
| 				break; | ||||
| 			case '\t' : case '\r' : case '\n' : case ':' : case ',': case ' ':  | ||||
| 				break; | ||||
| #ifdef JSMN_STRICT | ||||
| 			/* In strict mode primitives are: numbers and booleans */ | ||||
| 			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' : | ||||
| #else | ||||
| 			/* In non-strict mode every unquoted value is a primitive */ | ||||
| 			default: | ||||
| #endif | ||||
| 				r = jsmn_parse_primitive(parser, js, tokens, num_tokens); | ||||
| 				if (r < 0) return r; | ||||
| 				if (parser->toksuper != -1) | ||||
| 					tokens[parser->toksuper].size++; | ||||
| 				break; | ||||
|  | ||||
| #ifdef JSMN_STRICT | ||||
| 			/* Unexpected char in strict mode */ | ||||
| 			default: | ||||
| 				return JSMN_ERROR_INVAL; | ||||
| #endif | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for (i = parser->toknext - 1; i >= 0; i--) { | ||||
| 		/* Unmatched opened object or array */ | ||||
| 		if (tokens[i].start != -1 && tokens[i].end == -1) { | ||||
| 			return JSMN_ERROR_PART; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return JSMN_SUCCESS; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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; | ||||
| } | ||||
|  | ||||
							
								
								
									
										67
									
								
								utility/jsmn.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								utility/jsmn.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #ifndef __JSMN_H_ | ||||
| #define __JSMN_H_ | ||||
|  | ||||
| /** | ||||
|  * JSON type identifier. Basic types are: | ||||
|  * 	o Object | ||||
|  * 	o Array | ||||
|  * 	o String | ||||
|  * 	o Other primitive: number, boolean (true/false) or null | ||||
|  */ | ||||
| typedef enum { | ||||
| 	JSMN_PRIMITIVE = 0, | ||||
| 	JSMN_OBJECT = 1, | ||||
| 	JSMN_ARRAY = 2, | ||||
| 	JSMN_STRING = 3 | ||||
| } jsmntype_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	/* Not enough tokens were provided */ | ||||
| 	JSMN_ERROR_NOMEM = -1, | ||||
| 	/* Invalid character inside JSON string */ | ||||
| 	JSMN_ERROR_INVAL = -2, | ||||
| 	/* The string is not a full JSON packet, more bytes expected */ | ||||
| 	JSMN_ERROR_PART = -3, | ||||
| 	/* Everything was fine */ | ||||
| 	JSMN_SUCCESS = 0 | ||||
| } jsmnerr_t; | ||||
|  | ||||
| /** | ||||
|  * JSON token description. | ||||
|  * @param		type	type (object, array, string etc.) | ||||
|  * @param		start	start position in JSON data string | ||||
|  * @param		end		end position in JSON data string | ||||
|  */ | ||||
| typedef struct { | ||||
| 	jsmntype_t type; | ||||
| 	int start; | ||||
| 	int end; | ||||
| 	int size; | ||||
| #ifdef JSMN_PARENT_LINKS | ||||
| 	int parent; | ||||
| #endif | ||||
| } jsmntok_t; | ||||
|  | ||||
| /** | ||||
|  * JSON parser. 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 */ | ||||
| 	int toknext; /* next token to allocate */ | ||||
| 	int toksuper; /* superior token node, e.g parent object or array */ | ||||
| } jsmn_parser; | ||||
|  | ||||
| /** | ||||
|  * Create JSON parser over an array of tokens | ||||
|  */ | ||||
| void jsmn_init(jsmn_parser *parser); | ||||
|  | ||||
| /** | ||||
|  * Run JSON parser. It parses a JSON data string into and array of tokens, each describing | ||||
|  * a single JSON object. | ||||
|  */ | ||||
| jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js,  | ||||
| 		jsmntok_t *tokens, unsigned int num_tokens); | ||||
|  | ||||
| #endif /* __JSMN_H_ */ | ||||
		Reference in New Issue
	
	Block a user