123
MbedJSONValue.h
- Committer:
- samux
- Date:
- 2011-09-22
- Revision:
- 0:0cf0e27feaad
- Child:
- 1:effaca3e3f84
File content as of revision 0:0cf0e27feaad:
/** * @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_