JSON library based on JSMN lib

Dependents:   ATT_WNCInterface_Info WNCInterface_HTTP_example NerfUS-Coord Mbed_Prototype_copy_4_INNO_day_15_6_2017 ... more

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.

Revision:
5:dd98cf00ed9b
Parent:
4:ae34010d87e5
Child:
6:c1d2153da4ed
--- a/Json.h	Tue Aug 02 20:21:04 2016 +0000
+++ b/Json.h	Mon Aug 15 22:50:26 2016 +0000
@@ -1,3 +1,25 @@
+/* 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_
 
@@ -6,88 +28,372 @@
 #include <string.h>
 
 /**
- * JSON wrapper over JSMN lib
+ * 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.
  */
 
 class Json
 {
     private:
+        const unsigned int maxTokenCount;
         const char * source;
         const size_t sourceLength;
         jsmntok_t * tokens;
         int tokenCount;
-        Json ( const Json & other );
+
+        // 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 it's working, in favor of small memory fottprints, 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
+        /** 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 ( const char * jsonString, size_t length );
+        Json ( const char * jsonString, size_t length, unsigned int maxTokens = 32 );
+
+
+        /** 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 ();
 
-        int findKeyIndex ( const char * key, const int &startingAt ) const;
-        int findKeyIndexIn ( const char * key, const int &parentIndex ) const;
-        int findChildIndexOf ( const int &parentIndex, const int &startingAt ) const;
+
+        /** 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;
 
-        int tokenIntegerValue ( const int tokenIndex ) const;
-        float tokenNumberValue ( 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.
 
-        inline bool tokenBooleanValue ( const int tokenIndex ) const;
+         @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.
 
-        // void print () const;
-        
+         @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.
+         */
         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
 {
-    return tokens [ tokenIndex ].type;
+    jsmntype_t retVal = JSMN_UNDEFINED;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].type;
+    }
+
+    return retVal;
 }
 
 inline int Json::parent ( const int tokenIndex ) const
 {
-    return tokens [ tokenIndex ].parent;
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].parent;
+    }
+
+    return retVal;
 }
 
 inline int Json::childCount ( const int tokenIndex ) const
 {
-    return tokens [ tokenIndex ].childCount;
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].childCount;
+    }
+
+    return retVal;
 }
 
 inline int Json::tokenLength ( const int tokenIndex ) const
 {
-    return tokens [ tokenIndex ].end - tokens [ tokenIndex ].start;
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].end - tokens [ tokenIndex ].start;
+    }
+
+    return retVal;
 }
 
 inline const char * Json::tokenAddress ( const int tokenIndex ) const
 {
-    return source + tokens [ tokenIndex ].start;
-}
+    char * retVal = NULL;
 
-inline bool Json::tokenBooleanValue ( const int tokenIndex ) const
-{
-    if ( type ( tokenIndex ) == JSMN_PRIMITIVE )
+    if ( isValidToken ( tokenIndex ) )
     {
-        return matches ( tokenIndex, "true" );
+        retVal = (char *) source + tokens [ tokenIndex ].start;
     }
-    return false;
+
+    return retVal;
 }
 
 #endif