json lib

Dependents:   grove_stream_jpa_sd2 grove_stream_jpa_sd2 grove_stream_jpa_sd2-2 grove_stream_jpa_sd2-3 ... more

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