Contains json parsing code which can parse members as well as sub members.
Revision 0:c1cd8e6ecdc9, committed 2017-05-25
- Comitter:
- Amod Amatya amodamatya@gmail.com
- Date:
- Thu May 25 15:14:50 2017 +0545
- Commit message:
- ini
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Json.cpp Thu May 25 15:14:50 2017 +0545 @@ -0,0 +1,308 @@ +/* Json.cpp */ +/* Original Author: Faheem Inayat + * Created by "Night Crue" Team @ TechShop San Jose, CA + * + * MIT License + * + * 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 "Json.h" +#include <stdio.h> + + +// Json::Json() : maxTokenCount ( 0 ), sourceLength ( 0 ) +// { + +// } +// Json::Json() :maxTokenCount ( 0 ), source ( NULL ), sourceLength ( 0 ) +// { +// tokenCount = 0; +// tokens = NULL; +// } + +Json::Json ( const char * jsonString, size_t length, unsigned int maxTokens ) + : maxTokenCount ( maxTokens ), source ( jsonString ), sourceLength ( length ) +{ + jsmn_parser parser; + tokens = new jsmntok_t [ maxTokenCount ]; + + jsmn_init ( &parser ); + tokenCount = jsmn_parse ( &parser, jsonString, length, tokens, maxTokenCount ); +} + +Json::Json ( const Json & ) + : maxTokenCount ( 0 ), source ( NULL ), sourceLength ( 0 ) +{ + tokenCount = 0; + tokens = NULL; +} + +Json::~Json () +{ + delete [] tokens; +} + +int Json::findKeyIndex ( const char * key, const int &startingAt ) const +{ + int retVal = -1; + + int i = startingAt + 1; + if ( i < 0 ) { + i = 0; + } + + for ( ; i < tokenCount; i++ ) + { + jsmntok_t t = tokens [ i ]; + + if ( t.type == JSMN_KEY ) + { + size_t keyLength = (size_t) ( t.end - t.start ); + if ( ( strlen ( key ) == keyLength ) && ( strncmp ( source + t.start, key, keyLength ) == 0 ) ) + { + retVal = i; + break; + } + } + } + + return retVal; +} + +int Json::findKeyIndexIn ( const char * key, const int &parentIndex ) const +{ + int retVal = -1; + + if ( isValidToken ( parentIndex ) ) + { + for ( int i = parentIndex + 1; i < tokenCount; i++ ) + { + jsmntok_t t = tokens [ i ]; + + if ( t.end >= tokens [ parentIndex ].end ) + { + break; + } + + if ( ( t.type == JSMN_KEY ) && ( t.parent == parentIndex ) ) + { + size_t keyLength = (size_t) ( t.end - t.start ); + if ( ( strlen ( key ) == keyLength ) && ( strncmp ( source + t.start, key, keyLength ) == 0 ) ) + { + retVal = i; + break; + } + } + } + } + + return retVal; +} + +int Json::findChildIndexOf ( const int &parentIndex, const int &startingAt ) const +{ + int retVal = -1; + + if ( isValidToken ( parentIndex ) ) + { + + jsmntype_t type = tokens [ parentIndex ].type; + if ( ( type == JSMN_KEY ) || ( type == JSMN_OBJECT ) || ( type == JSMN_ARRAY ) ) + { + int i = startingAt + 1; + if ( startingAt < 0 ) + { + i = 0; + } + + for ( i += parentIndex; i < tokenCount; i++ ) + { + if ( tokens [ i ].parent == parentIndex ) + { + retVal = i; + break; + } + } + } + } + + return retVal; +} + +bool Json::matches ( const int & tokenIndex, const char * value ) const +{ + bool retVal = false; + + if ( isValidToken ( tokenIndex ) ) + { + jsmntok_t token = tokens [ tokenIndex ]; + retVal = ( strncmp ( source + token.start, value, ( token.end - token.start ) ) == 0 ); + } + + return retVal; +} + +int Json::tokenIntegerValue ( const int tokenIndex, int &returnValue ) const +{ + int retVal = -1; + + if ( type ( tokenIndex ) == JSMN_PRIMITIVE ) + { + int len = tokenLength ( tokenIndex ); + char * tok = new char [ len + 1 ]; + strncpy ( tok, tokenAddress ( tokenIndex ), len ); + tok [ len ] = 0; + returnValue = atoi ( tok ); + delete [] tok; + retVal = 0; + } + return retVal; +} + +int Json::tokenNumberValue ( const int tokenIndex, float &returnValue ) const +{ + int retVal = -1; + + if ( type ( tokenIndex ) == JSMN_PRIMITIVE ) + { + int len = tokenLength ( tokenIndex ); + char * tok = new char [ len + 1 ]; + strncpy ( tok, tokenAddress ( tokenIndex ), len ); + tok [ len ] = 0; + returnValue = atof ( tok ); + delete [] tok; + retVal = 0; + } + + return retVal; +} + +int Json::tokenBooleanValue ( const int tokenIndex, bool &returnValue ) const +{ + int retVal = -1; + + if ( type ( tokenIndex ) == JSMN_PRIMITIVE ) + { + returnValue = matches ( tokenIndex, "true" ); + retVal = 0; + } + + return retVal; +} + +char * Json::unescape ( char * jsonString ) +{ + if ( jsonString != NULL ) + { + int stringIndex = 0; + int indentLevel = 0; + int quoteCount = 0; + for ( int i = 0; jsonString [ i ] != 0; i ++ ) + { + switch ( jsonString [ i ] ) + { + case '{': + indentLevel ++; + break; + + case '}': + indentLevel --; + if ( indentLevel == 0 ) { + // Just close and return the first valid JSON object. No need to handle complex cases. + jsonString [ stringIndex ++ ] = '}'; + jsonString [ stringIndex ] = 0; + return jsonString; + } + break; + + case '\\': + i ++; + break; + + case '"': + quoteCount ++; + break; + } + + if ( indentLevel > 0 ) + { + if ( quoteCount == 0 ) { + return jsonString; //No need to unescape. JsonString needs to be already escaped + } + jsonString [ stringIndex ++ ] = jsonString [ i ]; + } + } + jsonString [ stringIndex ] = 0; + } + + return jsonString; +} + +const char * Json::JsonParse(const char * jsonString, const char * key) +{ + + Json json ( jsonString, strlen ( jsonString)); + + if ( !json.isValidJson () ) + { + printf( "Invalid JSON: %s", jsonString ); + } + + if ( json.type (0) != JSMN_OBJECT ) + { + printf ( "Invalid JSON. ROOT element is not Object: %s", jsonString ); + } + + char *info1 = new char[32]; + + // ROOT object should have '0' tokenIndex, and -1 parentIndex + int KeyIndex = json.findKeyIndexIn(key,0); + + if (KeyIndex == -1) + { + // Error handling part ... + printf ( "Key does not exist"); + } + else + { + // Find the first child index of key-node "info" + int ValueIndex = json.findChildIndexOf (KeyIndex, -1 ); + if (ValueIndex > 0 ) + { + const char * valueStart = json.tokenAddress (ValueIndex ); + int valueLength = json.tokenLength (ValueIndex ); + strncpy ( info1, valueStart, valueLength ); + // info[valueLength] = 0; // NULL-terminate the string + // strcpy(info1, info); + //let's print the value. It should be "San Jose" + // printf( "info: %s", info); + } + } + return info1; +} +// plublic: +// const char* getBar() const { +// char * str = new char[_sBar.length()+1]; +// strcpy(str, _sBar.c_str()); +// return str;} +// In main: + +// foo object; +// ///some code +// const char* bar = object.getBar(); +// ///some code +// delete [] bar; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Json.h Thu May 25 15:14:50 2017 +0545 @@ -0,0 +1,474 @@ +/* Json.h */ +/* Original Author: Faheem Inayat + * Created by "Night Crue" Team @ TechShop San Jose, CA + * + * MIT License + * + * 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 __JSON_LIB_CLASS_H_ +#define __JSON_LIB_CLASS_H_ + +#include "jsmn.h" + +#include <stdlib.h> +#include <string.h> + + +/** + * 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. + */ + + /* + Example: + + Let's say we have to parse the samle JSON: + + { + "team": "Night Crue", + "company": "TechShop", + "city": "San Jose", + "state": "California", + "country": "USA", + "zip": 95113, + "active": true, + "members": + [ + { + "firstName": "John", + "lastName": "Smith", + "active": false, + "hours": 18.5, + "age": 21 + }, + { + "firstName": "Foo", + "lastName": "Bar", + "active": true, + "hours": 25, + "age": 21 + }, + { + "firstName": "Peter", + "lastName": "Jones", + "active": false + } + ] + } + + which without the "white spaces" will look like: {"team":"Night Crue","company":"TechShop","city":"San Jose","state":"California","country":"USA","zip":95113,"active":true,"members":[{"firstName":"John","lastName":"Smith","active":false,"hours":18.5,"age":21},{"firstName":"Foo","lastName":"Bar","active":true,"hours":25,"age":21},{"firstName":"Peter","lastName":"Jones","active":false}]} + + Anyways, this class doesn't care about the formatting of JSON, however, it + DOES care about the validity of JSON. So here's a sample code to parse and + extract values from this JSON structure. + + @code + + void main () + { + const char *jsonSource = "{\"team\":\"Night Crue\",\"company\":\"TechShop\",\"city\":\"San Jose\",\"info\":{\"name\":\"Amod\",\"age\":23}}"; + + Json json(jsonSource, strlen(jsonSource)); + const char * a = json.JsonParse(jsonSource, "info"); + logInfo("Information is %s",a); + const char * b = json.JsonParse(a, "name"); + logInfo("Name is %s",b); + const char * c = json.JsonParse(a, "age"); + logInfo("Age is %s",c); + + // More on this example to come, later. + } + + @endcode + */ + +class Json +{ + + + private: + const unsigned int maxTokenCount; + const char * source; + const size_t sourceLength; + jsmntok_t * tokens; + int tokenCount; + + // Copy COntructor is intentionally kept private to enforce the caller + // to use pointers/reference, and never pass-by-value + Json ( const Json & ); + + + public: + /** The only constructor allowed. + As JSON object will create/allocate memory for its working, in favor of + small memory footprints, it is not allowed to be passed-by-value. So + there is no copy- or default-constructor + + @param jsonString char string containing JSON data + @param length length of the jsonString + @param maxTokens optional maximum count of Tokens. Default is 32. + */ + // Json (); + Json ( const char * jsonString, size_t length, unsigned int maxTokens = 32 ); + + // Json(); + + + /** Although there is no virtual function to this class, destructor is + still made virtual, for just-in-case use. Destructor will delete the + 'tokens' array, created in constructor. + */ + virtual ~Json (); + + + /** findKeyIndex will find and return the token index representing the + 'Key' in underlying JSON object. It is a strictly a linear key search + and will return the first occurrence, without the JSON node structure + semantics. For search in a specific node, refer to #findKeyIndex + + @param key a char string to find as a 'Key' in JSON structure. + @param startingAt the starting token-index for 'key' search. The + search will NOT include this index, but instead will use the + next one as the starting point. In case, a negative value is + passed, search will start from '0'. It's caller's + responsibility to make sure what values they're passing. + Default value is set to '0', as the zero-th token index in any + valid JSON object should always be starting object brace '{'. + So default behavior is to always find the very first occurrence + of key as represented by 'key' parameter. + + @return a non-zero positive integer, if a key is found in the source + JSON. If no key is found, -1 will be returned. There should be + no '0' value returned in any valid case. + */ + int findKeyIndex ( const char * key, const int &startingAt = 0 ) const; + + + /** findKeyIndexIn will find and return the token index representing the + 'Key' in underlying JSON object node. It is strictly a single-level + key search function, and will NOT look for the key in any child JSON + nodes (JSON Object/Array). + + @param key a char string to find as a 'Key' in JSON structure. + @param parentIndex the starting token-index for 'key' search. The + search will look for the key, only under the JSON node + represented by this parentIndex. Default value is '0', making + the default behavior to look for only root-level keys. The + valid value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return a non-zero positive integer, if a key is found in the source + JSON. If no key is found, -1 will be returned. There should be + no '0' value returned in any valid case. + */ + int findKeyIndexIn ( const char * key, const int &parentIndex = 0 ) const; + + + /** findChildIndexOf will find and return the token index representing + first child a JSON node represented by parentIndex (that is either a + Key, an Object, or an Array), and exists after the startingAt value. + This function is particularly handy in iterating over Array Objects, or + getting the index for JSON 'Value' of a JSON 'Key'. + + @param parentIndex token index representing the parent node in JSON + source. The valid value range is 0 to [parsedTokenCount()-1] + both inclusive. + @param startingAt describes the starting index of the nodes to search. + In other words, if caller wants to skip some nodes, they can + provide this value. Default value is 0, which means search for + all nodes in the parent. + + @return a non-zero positive integer, if the child node is found in + source JSON. If no child is found, -1 will be returned. There + should be no '0' value returned in any valid case. + */ + int findChildIndexOf ( const int &parentIndex, const int &startingAt = 0 ) const; + + + /** matches will tell if the token data (either key or value) matches + with the value provided. This function is particularly handy in + iterating over the keys, and finding a specific key, in an object. The + comparison is case-sensitive. i.e. 'Apple' will NOT match with 'apple'. + + @param tokenIndex representing the token to compare. The valid value + range is 0 to [parsedTokenCount()-1] both inclusive. + @param value to compare the token data with. + + @return true if the token data matches with value. false will be + returned either the value doesn't match OR the tokenIndex is + not valid. + */ + bool matches ( const int & tokenIndex, const char * value ) const; + + + /** parsedTokenCount will tell how many tokens have been parsed by JSMN + parser. It is a utility function, for token validity. + + @return non-negative integer number of tokens parsed by JSMN library. + Negative value may be returned in case of error, but this + behavior is not tested, yet. + */ + inline int parsedTokenCount () const; + + + /** isValidJson will tell the caller if the parsed JSON was valid, + parsed, and accepted to further work on. + + @return true if the JSON is valid, false otherwise. + */ + inline bool isValidJson () const; + + + /** isValidToken will tell the caller if the tokenIndex is in valid + range. The valid value range is 0 to [parsedTokenCount()-1] both + inclusive. + + @param tokenIndex representing the token in the JSON source + + @return true if the JSON is valid, false otherwise. + */ + inline bool isValidToken ( const int tokenIndex ) const; + + + /** type will return the JSMN type represented by the tokenIndex. + + @param tokenIndex representing the token in the JSON source. The valid + value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return the type represented by tokenIndex. In case of invalid + tokenIndex, JSMN_UNDEFINED is returned. + */ + inline jsmntype_t type ( const int tokenIndex ) const; + + + /** parent is a utility function to get the parent index of the + tokenIndex passed. + + @param tokenIndex representing the token in the JSON source. The valid + value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return the parentIndex if the node has a parent, and tokenIndex is a + valid index. In case of no parent, or invalid tokenIndex, -1 + is returned. + */ + inline int parent ( const int tokenIndex ) const; + + + /** childCount returns the number of children sharing the same parent. + This utility function is handy for iterating over Arrays or Objects. + + @param tokenIndex representing the token in the JSON source. The valid + value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return non-negative integer representing the number of children + tokenIndex node has. 0 is a valid number, in case the node has + no child nodes. -1 will be returned if the tokenIndex is not + valid. + */ + inline int childCount ( const int tokenIndex ) const; + + + /** tokenLength returns the number of characters a node takes up in JSON + source string. + + @param tokenIndex representing the token in the JSON source. The valid + value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return positive integer value representing the length of the token + sub-string in the source JSON. The 0 value is an invalid state + and should never occur. -1 will be returned in case of invalid + tokenIndex. + */ + inline int tokenLength ( const int tokenIndex ) const; + + + /** tokenAddress returns the pointer that marks as the start of token + in JSON source string. This is a utility function for character/string + manipulation by the caller. + + @param tokenIndex representing the token in the JSON source. The valid + value range is 0 to [parsedTokenCount()-1] both inclusive. + + @return a non-NULL pointer will be returned if tokenIndex is valid, -1 + otherwise. + */ + inline const char * tokenAddress ( const int tokenIndex ) const; + + + /** tokenInterValue will convert the value as int represented by the + tokenIndex. A typical use is that caller has found the Key-index, and + then has retrieved the Value-index (by using findChildIndexOf function) + , and now they want to read the value of Value-index, as integer value. + + @param tokenIndex representing the "value" in the JSON source. The + valid value range is 0 to [parsedTokenCount()-1] both inclusive. + + @param returnValue is a return-parameter passed by reference to hold up + the integer value parsed by this function. If the converted + value would be out of the range of representable values by an + int, it causes undefined behavior. It is caller's + responsibility to check for these cases. + + @return 0 if the operation is successful. -1 if tokenIndex is invalid. + */ + int tokenIntegerValue ( const int tokenIndex, int &returnValue ) const; + + + /** tokenNumberValue will convert the value as float represented by the + tokenIndex. A typical use is that caller has found the Key-index, and + then has retrieved the Value-index (by using findChildIndexOf function) + , and now they want to read the value of Value-index, as floating-point + value. + + @param tokenIndex representing the "value" in the JSON source. The + valid value range is 0 to [parsedTokenCount()-1] both inclusive. + + @param returnValue is a return-parameter passed by reference to hold up + the floating-point value parsed by this function. If the + converted value would be out of the range of representable + values by a float, it causes undefined behavior. It is caller's + responsibility to check for these cases. + + @return 0 if the operation is successful. -1 if tokenIndex is invalid. + */ + int tokenNumberValue ( const int tokenIndex, float &returnValue ) const; + + + /** tokenBooleanValue will convert the value as bool represented by + the tokenIndex. A typical use is that caller has found the Key-index, + and then has retrieved the Value-index (by using findChildIndexOf + function), and now they want to read the value of Value-index, as + boolean value. + + @param tokenIndex representing the "value" in the JSON source. The + valid value range is 0 to [parsedTokenCount()-1] both inclusive. + + @param returnValue is a return-parameter passed by reference to hold up + the bool value parsed by this function. + + @return 0 if the operation is successful. -1 if tokenIndex is invalid. + */ + int tokenBooleanValue ( const int tokenIndex, bool &returnValue ) const; + + + /** unescape is a utility function to unescape a JSON string. This + function does not change any state of Json object, and is a pure + static utility function. This function is in-pace unescaping, and WILL + modify the source parameter. + + @param jsonString representing an escaped JSON string. This parameter + is also the return parameter as well. All modifications will be + reflected in this parameter. + + @return pointer to unescaped JSON string. This is exactly the same + pointer as jsonString parameter. + */ + + const char * JsonParse(const char * jsonString, const char * key); + + static char * unescape ( char * jsonString ); +}; + +inline int Json::parsedTokenCount () const +{ + return tokenCount; +} + +inline bool Json::isValidJson () const +{ + return ( tokenCount >= 1 ); +} + +inline bool Json::isValidToken ( const int tokenIndex ) const +{ + return ( tokenIndex >= 0 && tokenIndex < tokenCount ); +} + +inline jsmntype_t Json::type ( const int tokenIndex ) const +{ + jsmntype_t retVal = JSMN_UNDEFINED; + + if ( isValidToken ( tokenIndex ) ) + { + retVal = tokens [ tokenIndex ].type; + } + + return retVal; +} + +inline int Json::parent ( const int tokenIndex ) const +{ + int retVal = -1; + + if ( isValidToken ( tokenIndex ) ) + { + retVal = tokens [ tokenIndex ].parent; + } + + return retVal; +} + +inline int Json::childCount ( const int tokenIndex ) const +{ + int retVal = -1; + + if ( isValidToken ( tokenIndex ) ) + { + retVal = tokens [ tokenIndex ].childCount; + } + + return retVal; +} + +inline int Json::tokenLength ( const int tokenIndex ) const +{ + int retVal = -1; + + if ( isValidToken ( tokenIndex ) ) + { + retVal = tokens [ tokenIndex ].end - tokens [ tokenIndex ].start; + } + + return retVal; +} + +inline const char * Json::tokenAddress ( const int tokenIndex ) const +{ + char * retVal = NULL; + + if ( isValidToken ( tokenIndex ) ) + { + retVal = (char *) source + tokens [ tokenIndex ].start; + } + + return retVal; +} + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jsmn.c Thu May 25 15:14:50 2017 +0545 @@ -0,0 +1,340 @@ +/* Author: Faheem Inayat + * Created by "Night Crue" Team @ TechShop San Jose, CA + * + * --- DISCLAIMER --- + * This code is a modified version of original JSMN lirary, written by + * *** Serge A. Zaitsev *** + * and hosted at https://github.com/zserge/jsmn + * Any modification to the original source is not guaranteed to be included + * in this version. As of writing of this file, the original source is + * licensed under MIT License + * (http://www.opensource.org/licenses/mit-license.php). + */ + +#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->childCount = 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->childCount = 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_ERROR_INVAL; + } + } + /* primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_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_ERROR_NOMEM; + } + jsmn_fill_token ( token, JSMN_PRIMITIVE, start, parser->pos ); + token->parent = parser->toksuper; + parser->pos--; + return 0; +} + +/** + * Fills 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, unsigned char isKey ) +{ + 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_ERROR_NOMEM; + } + if ( isKey == 1 ) + { + jsmn_fill_token ( token, JSMN_KEY, start + 1, parser->pos ); + } + else + { + jsmn_fill_token ( token, JSMN_STRING, start + 1, parser->pos ); + } + token->parent = parser->toksuper; + return 0; + } + + /* Backslash: Quoted symbol expected */ + if ( c == '\\' && parser->pos + 1 < len ) + { + int i; + 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++; + for ( i = 0; i < 4 && parser->pos < len && js [ parser->pos ] != '\0'; i++ ) + { + /* If it isn't a hex character we have an error */ + 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_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_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 = parser->toknext; + + unsigned char isKey = 1; + + for ( ; parser->pos < len && js [ parser->pos ] != '\0'; parser->pos++ ) + { + char c; + jsmntype_t type; + + 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_ERROR_NOMEM; + if ( parser->toksuper != -1 ) + { + tokens [ parser->toksuper ].childCount++; + token->parent = parser->toksuper; + } + token->type = ( c == '{' ? JSMN_OBJECT : JSMN_ARRAY ); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + if ( token->type == JSMN_OBJECT ) + { + isKey = 1; + } + break; + case '}': + case ']': + if ( tokens == NULL ) + break; + type = ( c == '}' ? JSMN_OBJECT : JSMN_ARRAY ); + 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 ]; + } + break; + case '\"': + r = jsmn_parse_string ( parser, js, len, tokens, num_tokens, isKey ); + if ( r < 0 ) + return r; + count++; + if ( parser->toksuper != -1 && tokens != NULL ) + tokens [ parser->toksuper ].childCount++; + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + isKey = 0; + parser->toksuper = parser->toknext - 1; + break; + case ',': + if ( tokens != NULL && parser->toksuper != -1 && tokens [ parser->toksuper ].type != JSMN_ARRAY && tokens [ parser->toksuper ].type != JSMN_OBJECT ) + { + parser->toksuper = tokens [ parser->toksuper ].parent; + } + isKey = 1; + break; + /* 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': + /* And they must not be keys of the object */ + if ( tokens != NULL && parser->toksuper != -1 ) + { + jsmntok_t *t = &tokens [ parser->toksuper ]; + if ( t->type == JSMN_OBJECT || ( t->type == JSMN_STRING && t->childCount != 0 ) ) + { + return JSMN_ERROR_INVAL; + } + } + 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 ].childCount++; + break; + + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; + } + } + + if ( tokens != NULL ) + { + 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 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 Thu May 25 15:14:50 2017 +0545 @@ -0,0 +1,96 @@ +/* Author: Faheem Inayat + * Created by "Night Crue" Team @ TechShop San Jose, CA + * + * --- DISCLAIMER --- + * This code is a modified version of original JSMN lirary, written by + * *** Serge A. Zaitsev *** + * and hosted at https://github.com/zserge/jsmn + * Any modification to the original source is not guaranteed to be included + * in this version. As of writing of this file, the original source is + * licensed under MIT License + * (http://www.opensource.org/licenses/mit-license.php). + */ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" +{ +#endif +/* + Modified version of original jsmn lib ... added a type "JSMN_KEY" and enabled + parent-pointers and strict JSON check. +*/ + /** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ + typedef enum + { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4, + JSMN_KEY = 5 + } jsmntype_t; + + enum jsmnerr + { + /* 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 + }; + + /** + * 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 parent; + int childCount; + } 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 */ + unsigned 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. + */ + int jsmn_parse ( jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens ); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ +