123
Diff: MbedJSONValue.h
- 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_