Minimalist JSON parser and serializer (inspired by picojson). Used by MbedJSONRpc.

Dependents:   RPC_mbed_client WebSocket_test pseudo_comet RPC_Wifly_HelloWorld ... more

Revision:
0:0cf0e27feaad
Child:
1:effaca3e3f84
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MbedJSONValue.h	Thu Sep 22 10:35:59 2011 +0000
@@ -0,0 +1,568 @@
+/**
+* @author Samuel Mokrani
+*
+* @section LICENSE
+*
+* Copyright (c) 2011 mbed
+*
+* 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.
+*
+* @section DESCRIPTION
+*    Types Abstraction. JSON serializer and parser. (inspired by picojson). Is used by MbedJSONRpc.
+*
+*/
+
+#ifndef _Mbed_RPC_VALUE_H_
+#define _Mbed_RPC_VALUE_H_
+
+#define NB_TOKEN 20
+/*!< Number maximum of MbedJSONValue in an array or an object */
+
+#include <string>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/** MbedJSONValue class
+ *
+ * Example:
+ *    - creation of an MbedJSONValue of type TypeObject containing two others MbedJSONValue: 
+ *         -one array of one string and one integer identified by "my_array"
+ *         -a boolean identified by "my_boolean"
+ *    - serialization in JSON format of this object
+ * @code
+ *     MbedJSONValue demo;
+ *     std::string s;
+ *
+ *     //fill the object
+ *     demo["my_array"][0] = "demo_string";
+ *     demo["my_array"][1] = 10;
+ *     demo["my_boolean"] = true;
+ *
+ *     //serialize it into a JSON string
+ *     s = demo.serialize();
+ *
+ *  
+ * @endcode
+ *
+ * Example:
+ *     - creation of an MbedJSONValue from a JSON string
+ *     - extraction of different values from this existing MbedJSONValue
+ * @code
+ *
+ *     MbedJSONValue demo;
+ *
+ *     const  char * json = "{\"my_array\": [\"demo_string\", 10], \"my_boolean\": true}";
+ *
+ *     //parse the previous string and fill the object demo
+ *     parse(demo, json);
+ *
+ *     std::string my_str;
+ *     int my_int;
+ *     bool my_bool;
+ *
+ *     my_str = demo["my_array"][0];
+ *     my_int = demo["my_array"][1];
+ *     my_bool = demo["my_boolean"];
+ *
+ * @endcode
+ */
+class MbedJSONValue {
+public:
+
+    /**
+    * \enum Type
+    * \brief All types which can be used
+    */
+    enum Type {
+        TypeNull,     /*!< Null type */
+        TypeBoolean,  /*!< Boolean */
+        TypeInt,      /*!< Integer */
+        TypeDouble,   /*!< Double */
+        TypeString,   /*!< String */
+        TypeArray,    /*!< Array of Type */
+        TypeObject    /*!< Object */
+    };
+
+    /**
+    * MbedJSONValue constructor of type TypeNull
+    */
+    MbedJSONValue() : _type(TypeNull), index_array(0), index_token(0) {}
+    
+    /**
+    * MbedJSONValue constructor of type TypeBoolean
+    *
+    * @param value the object created will be initialized with this boolean
+    */
+    MbedJSONValue(bool value) : _type(TypeBoolean), index_array(0), index_token(0) {
+        _value.asBool = value;
+    }
+    
+    /**
+    * MbedJSONValue constructor of type TypeInt
+    *
+    * @param value the object created will be initialized with this integer
+    */
+    MbedJSONValue(int value)  : _type(TypeInt), index_array(0), index_token(0) {
+        _value.asInt = value;
+    }
+    
+    /**
+    * MbedJSONValue constructor of type TypeDouble
+    *
+    * @param value the object created will be initialized with this double
+    */
+    MbedJSONValue(double value)  : _type(TypeDouble), index_array(0), index_token(0) {
+        _value.asDouble = value;
+    }
+
+    /**
+    * MbedJSONValue constructor of type TypeString
+    *
+    * @param value the object created will be initialized with this string
+    */
+    MbedJSONValue(std::string const& value) : _type(TypeString), index_array(0), index_token(0) {
+        _value.asString = new std::string(value);
+    }
+
+    /**
+    * MbedJSONValue constructor of type TypeString
+    *
+    * @param value the object created will be initialized with this string
+    */
+    MbedJSONValue(const char* value)  : _type(TypeString), index_array(0), index_token(0) {
+        _value.asString = new std::string(value);
+    }
+
+    /**
+    * Copy constructor
+    *
+    * @param rhs object which will be copied
+    */
+    MbedJSONValue(MbedJSONValue const& rhs) : _type(TypeNull) {  *this = rhs;  }
+
+    /**
+    * Destructor
+    */
+    ~MbedJSONValue() {  clean();  }
+
+    /**
+    * = Operator overloading for an MbedJSONValue from an MbedJSONValue
+    *
+    * @param rhs object
+    * @return a reference on the MbedJSONValue affected
+    */
+    MbedJSONValue& operator=(MbedJSONValue const & rhs);
+    
+    /**
+    * = Operator overloading for an MbedJSONValue from an int
+    *
+    * @param rhs integer
+    * @return a reference on the MbedJSONValue affected
+    */
+    MbedJSONValue& operator=(int const& rhs) { return operator=(MbedJSONValue(rhs));  }
+    
+    /**
+    * = Operator overloading for an MbedJSONValue from a double
+    *
+    * @param rhs double
+    * @return a reference on the MbedJSONValue affected
+    */
+    MbedJSONValue& operator=(double const& rhs) {  return operator=(MbedJSONValue(rhs));  }
+    
+    /**
+    * = Operator overloading for an MbedJSONValue from a string
+    *
+    * @param rhs string
+    * @return a reference on the MbedJSONValue affected
+    */
+    MbedJSONValue& operator=(const char* rhs) {  return operator=(MbedJSONValue(std::string(rhs))); }
+    
+    
+    /**
+    * [] Operator overloading for an MbedJSONValue.
+    * Each TypeObject object can contain an array of NB_TOKEN MbedJSONValue. 
+    * This operator is useful to create an array or to retrieve an MbedJSONValue of an existing array.
+    *
+    * @param i index of the array
+    * @return a reference on the MbedJSONValue created or retrieved
+    */
+    MbedJSONValue& operator[](int i);
+    
+    /**
+    * [] Operator overloading for an MbedJSONValue.
+    * Each TypeObject MbedJSONValue can contain NB_TOKEN MbedJSONValue IDENTIFIED BY A NAME 
+    * This operator is useful to create a TypeObject MbedJSONValue or to retrieve an MbedJSONValue of an existing TypeObject.
+    *
+    *
+    * @param str identifier of the sub MbedJSONValue
+    * @return a reference on the MbedJSONValue created or retrieved
+    */
+    MbedJSONValue& operator[](std::string str);
+
+    /**
+    * Retrieve the value of an MbedJSONValue object.
+    *
+    * Let's suppose that we have an MbedJSONValue of type TypeString.
+    * To retrieve this string, you have to do:
+    *   my_obj.get<std::string>();
+    *
+    * @return A contant reference on the value of the object
+    */
+    template <typename T> const T& get() const;
+    
+    /**
+    * Retrieve the value of an MbedJSONValue object.
+    *
+    * Let's suppose that we have an MbedJSONValue of type TypeInt.
+    * To retrieve this integer, you have to do:
+    *   my_obj.get<int>();
+    *
+    * @return A reference on the value of the object
+    */
+    template <typename T> T& get();
+
+
+    /**
+    * Return the type of the MbedJSONValue object
+    *
+    * @return type of the MbedJSONValue object
+    */
+    Type const &getType() const {
+        return _type;
+    }
+
+    /**
+    * Return the size of an MbedJSONValue object (works for TypeString, TypeArray or TypeObject) 
+    *
+    * @return size
+    */
+    int size() const;
+
+    /**
+    * Check for the existence in a TypeObject object of member identified by name
+    *
+    * @param name Identifier
+    * @return true if the object is of type TypeObject AND contains a member named "name", false otherwise
+    */
+    bool hasMember(char * name);
+
+    /**
+    * Convert an MbedJSONValue in a JSON frame
+    *
+    * @return JSON string
+    */
+    std::string serialize();
+
+protected:
+
+    // object type
+    Type _type;
+    
+    //indexes of TypeObject and TypeArray
+    int index_array;
+    int index_token;
+
+    //an object can contain NB_TOKEN tokens. Each token have a name
+    MbedJSONValue * token[NB_TOKEN];
+    std::string * token_name[NB_TOKEN];
+
+    //an object can contain an array of NB_TOKEN elements
+    MbedJSONValue * array[NB_TOKEN];
+
+    // Clean up
+    void clean();
+
+    union {
+        bool          asBool;
+        int           asInt;
+        double        asDouble;
+        std::string*  asString;
+    } _value;
+
+
+    MbedJSONValue& operator[](int i) const { return *(array[i]); }
+    MbedJSONValue& operator[](std::string k) const;
+    
+    std::string to_str();
+    void serialize(std::back_insert_iterator<std::string> os);
+
+};
+
+
+#define GET(ctype, var)                          \
+  template <> inline const ctype& MbedJSONValue::get<ctype>() const { \
+    return var;                              \
+  }                                  \
+  template <> inline ctype& MbedJSONValue::get<ctype>() {          \
+    return var;                              \
+  }
+GET(bool, _value.asBool)
+GET(double, _value.asDouble)
+GET(int, _value.asInt)
+GET(std::string, *_value.asString)
+#undef GET
+
+
+//Input class for JSON parser
+class input {
+protected:
+    const char * cur_;
+    const char * end_;
+    int last_ch_;
+    bool ungot_;
+    int line_;
+public:
+    input(const char * first, const char * last) : cur_(first), end_(last), last_ch_(-1), ungot_(false), line_(1) {};
+
+    int getc() {
+        if (ungot_) {
+            ungot_ = false;
+            return last_ch_;
+        }
+        if (cur_ == end_) {
+            last_ch_ = -1;
+            return -1;
+        }
+        if (last_ch_ == '\n') {
+            line_++;
+        }
+        last_ch_ = *cur_++ & 0xff;
+        return last_ch_;
+    }
+
+    void ungetc() {
+        if (last_ch_ != -1) {
+            ungot_ = true;
+        }
+    }
+
+    const char * cur() const {
+        return cur_;
+    }
+    int line() const {
+        return line_;
+    }
+    void skip_ws() {
+        while (1) {
+            int ch = getc();
+            if (! (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
+                ungetc();
+                break;
+            }
+        }
+    }
+    int expect(int expect) {
+        skip_ws();
+        if (getc() != expect) {
+            ungetc();
+            return false;
+        }
+        return true;
+    }
+    bool match(const std::string& pattern) {
+        for (std::string::const_iterator pi(pattern.begin());
+                pi != pattern.end();
+                ++pi) {
+            if (getc() != *pi) {
+                ungetc();
+                return false;
+            }
+        }
+        return true;
+    }
+};
+
+
+
+inline const char * parse(MbedJSONValue& out, const char * first, const char * last, std::string* err);
+
+/**
+* JSON string parser and creation of an MbedJSONValue
+*
+* @param out reference of an MbedJSONValue which will be filled according to the JSON string
+* @param str JSON string
+* @return A non empty string if there is a parsing error
+*
+*/
+
+inline std::string parse(MbedJSONValue& out, const char * str);
+inline bool _parse(MbedJSONValue& out, input& in);
+inline bool _parse_number(MbedJSONValue& out, input& in);
+inline bool _parse_string(MbedJSONValue& out, input& in);
+inline bool _parse_array(MbedJSONValue& out, input& in);
+inline bool _parse_object(MbedJSONValue& out, input& in);
+
+
+inline bool _parse_string(MbedJSONValue& out, input& in) {
+#ifdef DEBUG
+    printf("string detected\r\n");
+#endif
+    out = MbedJSONValue(std::string(""));
+    std::string& s = out.get<std::string>();
+    while (1) {
+        int ch = in.getc();
+        if (ch < ' ') {
+            in.ungetc();
+            return false;
+        } else if (ch == '"') {
+            return true;
+        } else if (ch == '\\') {
+            if ((ch = in.getc()) == -1) {
+                return false;
+            }
+            switch (ch) {
+#define MAP(sym, val) case sym: s.push_back(val); break
+                    MAP('"', '\"');
+                    MAP('\\', '\\');
+                    MAP('/', '/');
+                    MAP('b', '\b');
+                    MAP('f', '\f');
+                    MAP('n', '\n');
+                    MAP('r', '\r');
+                    MAP('t', '\t');
+#undef MAP
+                default:
+                    return false;
+            }
+        } else {
+            s.push_back(ch);
+        }
+    }
+}
+
+inline bool _parse_array(MbedJSONValue& out, input& in) {
+#ifdef DEBUG
+    printf("array detected\r\n");
+#endif
+    int i = 0;
+    if (in.expect(']')) {
+        return true;
+    }
+    do {
+        if (! _parse(out[i], in)) {
+            return false;
+        }
+        i++;
+    } while (in.expect(','));
+    return in.expect(']');
+}
+
+inline bool _parse_object(MbedJSONValue& out, input& in) {
+#ifdef DEBUG
+    printf("object detected\r\n");
+#endif
+    if (in.expect('}')) {
+        return true;
+    }
+    do {
+        MbedJSONValue key, val;
+        if (in.expect('"') && _parse_string(key, in) && in.expect(':') && _parse(val, in)) {
+            out[key.get<std::string>().c_str()] = val;
+#ifdef DEBUG
+            printf("key: %s \r\n", key.get<std::string>().c_str());
+#endif
+        } else {
+            return false;
+        }
+    } while (in.expect(','));
+    return in.expect('}');
+}
+
+inline bool _parse_number(MbedJSONValue& out, input& in) {
+#ifdef DEBUG
+    printf("number detected\r\n");
+#endif
+    std::string num_str;
+    while (1) {
+        int ch = in.getc();
+        if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == '.'
+                || ch == 'e' || ch == 'E') {
+            num_str.push_back(ch);
+        } else {
+            in.ungetc();
+            break;
+        }
+    }
+    char* endp;
+    if (strchr(num_str.c_str(), '.') != NULL || strchr(num_str.c_str(), 'e') != NULL || strchr(num_str.c_str(), '+') != NULL)
+        out = MbedJSONValue(strtod(num_str.c_str(), &endp));
+    else
+        out = MbedJSONValue((int)strtod(num_str.c_str(), &endp));
+    return endp == num_str.c_str() + num_str.size();
+}
+
+inline bool _parse(MbedJSONValue& out, input& in) {
+    in.skip_ws();
+    int ch = in.getc();
+    switch (ch) {
+#define IS(ch, text, val) case ch: \
+      if (in.match(text)) { \
+    out = val; \
+    return true; \
+      } else { \
+    return false; \
+      }
+            IS('n', "ull", MbedJSONValue());
+            IS('f', "alse", MbedJSONValue(false));
+            IS('t', "rue", MbedJSONValue(true));
+#undef IS
+        case '"':
+            return _parse_string(out, in);
+        case '[':
+            return _parse_array(out, in);
+        case '{':
+            return _parse_object(out, in);
+        default:
+            if (('0' <= ch && ch <= '9') || ch == '-') {
+                in.ungetc();
+                return _parse_number(out, in);
+            }
+            break;
+    }
+    in.ungetc();
+    return false;
+}
+
+inline std::string parse(MbedJSONValue& out, const char * pos) {
+    const char * last = pos + strlen(pos);
+    std::string err;
+    pos = parse(out, pos, last, &err);
+    return err;
+}
+
+inline const char * parse(MbedJSONValue& out, const char * first, const char * last, std::string* err) {
+    input in = input(first, last);
+    if (! _parse(out, in) && err != NULL) {
+        char buf[64];
+        sprintf(buf, "syntax error at line %d near: ", in.line());
+        *err = buf;
+        while (1) {
+            int ch = in.getc();
+            if (ch == -1 || ch == '\n') {
+                break;
+            } else if (ch >= ' ') {
+                err->push_back(ch);
+            }
+        }
+    }
+    return in.cur();
+}
+
+#endif // _MbedMbedJSONValue_H_