|
- ///////////////////////////////////////////////////////////////////////////////
- //
- // Copyright (C) 2008-2012 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
- //
- // See accompanying file COPYING.TXT file for licensing details.
- //
- ///////////////////////////////////////////////////////////////////////////////
- #ifndef CPPCMS_JSON_H
- #define CPPCMS_JSON_H
-
- #include <cppcms/defs.h>
- #include <cppcms/string_key.h>
- #include <booster/copy_ptr.h>
- #include <booster/backtrace.h>
- #include <vector>
- #include <map>
- #include <string>
- #include <iostream>
- #include <limits>
-
- namespace cppcms {
- ///
- /// \brief This namespace includes all JSON parsing and formatting related classes and functions
- ///
- namespace json {
-
-
- class value;
-
- ///
- /// \brief Special object that is convertible to null json value
- ///
- struct null {};
- ///
- /// \brief Special object that is convertible to undefined json value
- ///
- struct undefined {};
-
- inline bool operator==(undefined const &/*l*/,undefined const &/*r*/) {return true;}
- inline bool operator!=(undefined const &/*l*/,undefined const &/*r*/) {return false;}
- inline bool operator==(null const &/*l*/,null const &/*r*/) {return true;}
- inline bool operator!=(null const &/*l*/,null const &/*r*/) {return false;}
-
- ///
- /// \brief The json::array - std::vector of json::value's
- ///
- typedef std::vector<value> array;
- ///
- /// \brief The json::object - std::map of json::value's
- ///
- typedef std::map<string_key,value> object;
-
- #ifdef CPPCMS_DOXYGEN_DOCS
-
- ///
- /// \brief The type traits schema for converting json values to/from orinary objects
- /// i.e. serialization from JSON to C++ object
- ///
- template<typename T>
- struct traits {
- ///
- /// Convert json value \a v to an object of type T and return it
- ///
- static T get(value const &v);
- ///
- /// Convert an object \a in to a json::value \a v
- ///
- static void set(value &v,T const &in);
- };
-
- #else
-
- template<typename T>
- struct traits;
-
- #endif
-
-
- ///
- /// The type of json value
- ///
- typedef enum {
- is_undefined, ///< Undefined value
- is_null, ///< null value
- is_boolean, ///< boolean value
- is_number, ///< numeric value
- is_string, ///< string value
- is_object, ///< object value
- is_array ///< array value
- } json_type;
-
-
- enum {
- compact = 0, ///< Print JSON values in most compact format
- readable = 1 ///< Print JSON values in human readable format (with identention)
- };
-
- ///
- /// \brief The error that is thrown in case of bad conversion of json::value to ordinary value
- ///
- /// When implementing json::traits for complex classes you are expected to throw this exception
- /// in case of invalid formatting
- ///
- class CPPCMS_API bad_value_cast : public booster::bad_cast {
- public:
- bad_value_cast();
- bad_value_cast(std::string const &s);
- bad_value_cast(std::string const &s,json_type actual);
- bad_value_cast(std::string const &s,json_type expected, json_type actual);
-
- virtual ~bad_value_cast() throw();
- virtual const char* what() const throw();
- private:
- std::string msg_;
- };
-
- class value;
-
- ///
- /// Read json object from input stream
- ///
- std::istream CPPCMS_API &operator>>(std::istream &in,value &v);
-
- ///
- /// Write json object to output stream
- ///
- std::ostream CPPCMS_API &operator<<(std::ostream &out,value const &v);
-
- ///
- /// Write human readable representation of json_type
- ///
- std::ostream CPPCMS_API &operator<<(std::ostream &out,json_type);
-
- ///
- /// \brief This class is central representation of json objects.
- ///
- /// It can be a value from any type
- /// including scalar, object, array and undefined
- ///
- class CPPCMS_API value {
- public:
-
- ///
- /// Get the type of the value
- ///
- json_type type() const;
-
- ///
- /// Returns true if type()==json::is_undefined
- ///
- bool is_undefined() const;
- ///
- /// Returns true if type()==json::is_null
- ///
- bool is_null() const;
-
- ///
- /// Convert value to boolean, throws bad_value_cast if value's type is not boolean
- ///
- bool const &boolean() const;
- ///
- /// Convert value to double, throws bad_value_cast if value's type is not number
- ///
- double const &number() const;
- ///
- /// Convert value to strng, throws bad_value_cast if value's type is not string
- ///
- std::string const &str() const;
- ///
- /// Convert value to json::object, throws bad_value_cast if value's type is not object
- ///
- json::object const &object() const;
- ///
- /// Convert value to json::array, throws bad_value_cast if value's type is not array
- ///
- json::array const &array() const;
-
- ///
- /// Get reference to bool variable that represents this value, throws bad_value_cast if type is invalid
- ///
- bool &boolean();
- ///
- /// Get reference to double variable that represents this value, throws bad_value_cast if type is invalid
- ///
- double &number();
- ///
- /// Get reference to string variable that represents this value, throws bad_value_cast if type is invalid
- ///
- std::string &str();
- ///
- /// Get reference to object variable that represents this value, throws bad_value_cast if type is invalid
- ///
- json::object &object();
- ///
- /// Get reference to array variable that represents this value, throws bad_value_cast if type is invalid
- ///
- json::array &array();
-
- ///
- /// Set value to undefined type
- ///
- void undefined();
- ///
- /// Set value to null type
- ///
- void null();
-
- ///
- /// Set value to boolean type and assign it
- ///
- void boolean(bool);
- ///
- /// Set value to numeric type and assign it
- ///
- void number(double );
- ///
- /// Set value to string type and assign it
- ///
- void str(std::string const &);
- ///
- /// Set value to object type and assign it
- ///
- void object(json::object const &);
- ///
- /// Set value to array type and assign it
- ///
- void array(json::array const &);
-
-
- ///
- /// Convert the value to type T, using json::traits, throws bad_value_cast if conversion is not possible
- ///
- template<typename T>
- T get_value() const
- {
- return traits<T>::get(*this);
- }
-
- ///
- /// Convert the object \a v of type T to the value
- ///
- template<typename T>
- void set_value(T const &v)
- {
- traits<T>::set(*this,v);
- }
-
- ///
- /// Searches a value in the path \a path
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") would return value of undefined type.
- ///
- value const &find(std::string const &path) const;
- ///
- /// Searches a value in the path \a path
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") would return value of undefined type.
- ///
- value const &find(char const *path) const;
-
- ///
- /// Searches a value in the path \a path, if not found throw bad_value_cast.
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") throws
- ///
- value const &at(std::string const &path) const;
- ///
- /// Searches a value in the path \a path, if not found throw bad_value_cast.
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") throws
- ///
- value const &at(char const *path) const;
- ///
- /// Searches a value in the path \a path, if not found throw bad_value_cast.
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") throws
- ///
- value &at(std::string const &path);
- ///
- /// Searches a value in the path \a path, if not found throw bad_value_cast.
- ///
- /// For example if the json::value represents { "x" : { "y" : 10 } }, then find("x.y") would return
- /// a reference to value that hold a number 10, find("x") returns a reference to a value
- /// that holds an object { "y" : 10 } and find("foo") throws
- ///
- value &at(char const *path);
-
- ///
- /// Sets the value \a v at the path \a path, if the path invalid, creates it.
- ///
- void at(std::string const &path,value const &v);
- ///
- /// Sets the value \a v at the path \a path, if the path invalid, creates it.
- ///
- void at(char const *path,value const &v);
-
-
- ///
- /// Creates a value from and object \a v of type T
- ///
- template<typename T>
- value(T const &v)
- {
- set_value(v);
- }
-
- ///
- /// Returns the type of variable in path, if not found returns undefined
- ///
- /// Same as find(path).type()
- ///
- json_type type(std::string const &path) const
- {
- return find(path).type();
- }
- ///
- /// Returns the type of variable in path, if not found returns undefined
- ///
- /// Same as find(path).type()
- ///
- json_type type(char const *path) const
- {
- return find(path).type();
- }
-
- ///
- /// Convert an object \a v of type T to a value at specific path, same as at(path,value(v))
- ///
- template<typename T>
- void set(std::string const &path,T const &v)
- {
- at(path,value(v));
- }
- ///
- /// Convert an object \a v of type T to a value at specific path, same as at(path,value(v))
- ///
- template<typename T>
- void set(char const *path,T const &v)
- {
- at(path,value(v));
- }
-
- ///
- /// Get a string value from a path \a path. If the path is not invalid or the object
- /// is not of type string at this path, returns \a def instead
- ///
- std::string get(std::string const &path,char const *def) const
- {
- value const &v=find(path);
- if(v.is_undefined())
- return def;
- try {
- return v.get_value<std::string>();
- }
- catch(std::bad_cast const &e) {
- return def;
- }
- }
- ///
- /// Get a string value from a path \a path. If the path is not invalid or the object
- /// is not of type string at this path, returns \a def instead
- ///
- std::string get(char const *path,char const *def) const
- {
- value const &v=find(path);
- if(v.is_undefined())
- return def;
- try {
- return v.get_value<std::string>();
- }
- catch(std::bad_cast const &e) {
- return def;
- }
- }
-
- ///
- /// Get an object of type T from the path \a path. Throws bad_value_cast if such path does not
- /// exists of conversion can't be done
- ///
- template<typename T>
- T get(std::string const &path) const
- {
- return at(path).get_value<T>();
- }
- ///
- /// Get an object of type T from the path \a path. Throws bad_value_cast if such path does not
- /// exists of conversion can't be done
- ///
- template<typename T>
- T get(char const *path) const
- {
- return at(path).get_value<T>();
- }
-
- ///
- /// Get an object of type T from the path \a path. Returns \a def if such path does not
- /// exists of conversion can't be done
- ///
- template<typename T>
- T get(char const *path,T const &def) const
- {
- value const &v=find(path);
- if(v.is_undefined())
- return def;
- try {
- return v.get_value<T>();
- }
- catch(std::bad_cast const &e) {
- return def;
- }
- }
- ///
- /// Get an object of type T from the path \a path. Returns \a def if such path does not
- /// exists of conversion can't be done
- ///
- template<typename T>
- T get(std::string const &path,T const &def) const
- {
- value const &v=find(path);
- if(v.is_undefined())
- return def;
- try {
- return v.get_value<T>();
- }
- catch(std::bad_cast const &e) {
- return def;
- }
- }
-
- ///
- /// Returns a reference to the node \a name of the value.
- /// For value = {"x",10} and name == "x" return a value that holds 10.
- ///
- /// If value is not object it's type set to object.
- /// If such node does not exits, it is created with undefined value
- ///
- value &operator[](std::string const &name);
-
- ///
- /// Returns reference to the node \a name of the value.
- /// For value = {"x",10} and name == "x" return a value that holds 10.
- ///
- /// If value is not object or such node does not exits, throws bad_value_cast
- ///
- value const &operator[](std::string const &name) const;
-
- ///
- /// Returns a reference to \a n 'th entry of the array. If the value is not an array it is reset to array,
- /// of the array is too small it is resized to size of at least n+1
- ///
- value &operator[](size_t n);
- ///
- /// Returns a reference to \a n 'th entry of array, if the value is not array or n is too big, throws
- /// bad_value_cast
- ///
- value const &operator[](size_t n) const;
-
- ///
- /// Convert a value to std::string, if \a how has value \a readable it is converted with indentation
- ///
- std::string save(int how=compact) const;
- ///
- /// Write a value to std::ostream, if \a how has value \a readable it is converted with indentation
- ///
- void save(std::ostream &out,int how=compact) const;
- ///
- /// Read a value from std::istream.
- ///
- /// Note: only JSON object and JSON array are considered valid values
- ///
- /// \param in the std::istream used to read the data
- /// \param full require EOF once the object is read, otherwise consider it as syntax error
- /// \param line_number return a number of the line where syntax error occurred
- /// \result returns true if the value was read successfully, otherwise returns false to indicate a syntax error.
- ///
- bool load(std::istream &in,bool full,int *line_number=0);
-
- ///
- /// Read a value from character range
- ///
- /// Note: only JSON object and JSON array are considered valid values
- ///
- /// \param begin - begin of the text range, at the end points to the end of parsed range
- /// \param end - end of the text range
- /// \param full require EOF once the object is read, otherwise consider it as syntax error
- /// \param line_number return a number of the line where syntax error occurred
- /// \result returns true if the value was read successfully, otherwise returns false to indicate a syntax error.
- ///
- /// \ver{v1_2}
- bool load(char const *&begin,char const *end,bool full,int *line_number=0);
-
- ///
- /// Compare two values objects, return true if they are same
- ///
- bool operator==(value const &other) const;
- ///
- /// Compare two values objects, return false if they are same
- ///
- bool operator!=(value const &other) const;
-
- ///
- /// Copy constructor
- ///
- value(value const &other) :
- d(other.d)
- {
- }
- ///
- /// Assignment operator
- ///
- value const &operator=(value const &other)
- {
- d=other.d;
- return *this;
- }
- ///
- /// Default value - creates a value of undefined type
- ///
- value()
- {
- }
-
- ///
- /// Destructor
- ///
-
- ~value()
- {
- }
-
- ///
- /// Swaps two values, does not throw.
- ///
- void swap(value &other)
- {
- d.swap(other.d);
- }
-
- private:
-
- void write(std::ostream &out,int tabs) const;
- void write_value(std::ostream &out,int tabs) const;
-
- struct _data;
- struct CPPCMS_API copyable {
-
- _data *operator->() { return &*d; }
- _data &operator*() { return *d; }
- _data const *operator->() const { return &*d; }
- _data const &operator*() const { return *d; }
-
- copyable();
- copyable(copyable const &r);
- copyable const &operator=(copyable const &r);
- ~copyable();
-
- void swap(copyable &other)
- {
- d.swap(other.d);
- }
- private:
- booster::copy_ptr<_data> d;
- } d;
-
- friend struct copyable;
- };
-
-
- ///
- /// Convert UTF-8 string to JSON string, i.e. a sring foo is converted to "foo",
- /// a string bar"baz is converted to "bar\"baz"
- ///
- /// \ver{v1_2}
- std::string CPPCMS_API to_json(std::string const &utf);
- ///
- /// Convert UTF-8 string to JSON string, i.e. a sring foo is converted to "foo",
- /// a string bar"baz is converted to "bar\"baz"
- ///
- /// \ver{v1_2}
- std::string CPPCMS_API to_json(char const *begin,char const *end);
- ///
- /// Convert UTF-8 string to JSON string, i.e. a sring foo is converted to "foo",
- /// a string bar"baz is converted to "bar\"baz"
- ///
- /// \ver{v1_2}
- void CPPCMS_API to_json(char const *begin,char const *end,std::ostream &out);
- //
- /// Convert UTF-8 string to JSON string, i.e. a sring foo is converted to "foo",
- /// a string bar"baz is converted to "bar\"baz"
- ///
- /// \ver{v1_2}
- void CPPCMS_API to_json(std::string const &str,std::ostream &out);
-
-
- /// \cond INTERNAL
-
- template<typename T1,typename T2>
- struct traits<std::pair<T1,T2> > {
- static std::pair<T1,T2> get(value const &v)
- {
- if(v.object().size()!=2)
- throw bad_value_cast("Object with two members expected");
- std::pair<T1,T2> pair(v.get_value<T1>("first"),v.get_value<T2>("second"));
- return pair;
- }
- static void set(value &v,std::pair<T1,T2> const &in)
- {
- v=json::object();
- v.set_value("first",in.first);
- v.set_value("second",in.second);
- }
- };
-
- template<typename T>
- struct traits<std::vector<T> > {
- static std::vector<T> get(value const &v)
- {
- std::vector<T> result;
- json::array const &a=v.array();
- result.resize(a.size());
- for(unsigned i=0;i<a.size();i++)
- result[i]=a[i].get_value<T>();
- return result;
- }
- static void set(value &v,std::vector<T> const &in)
- {
- v=json::array();
- json::array &a=v.array();
- a.resize(in.size());
- for(unsigned i=0;i<in.size();i++)
- a[i].set_value(in[i]);
- }
- };
-
-
- #define CPPCMS_JSON_SPECIALIZE(type,method) \
- template<> \
- struct traits<type> { \
- static type get(value const &v) \
- { \
- return v.method(); \
- } \
- static void set(value &v,type const &in)\
- { \
- v.method(in); \
- } \
- };
-
- CPPCMS_JSON_SPECIALIZE(bool,boolean);
- CPPCMS_JSON_SPECIALIZE(double,number);
- CPPCMS_JSON_SPECIALIZE(std::string,str);
- CPPCMS_JSON_SPECIALIZE(json::object,object);
- CPPCMS_JSON_SPECIALIZE(json::array,array);
-
- #undef CPPCMS_JSON_SPECIALIZE
-
- #define CPPCMS_JSON_SPECIALIZE_INT(type) \
- template<> \
- struct traits<type> { \
- static type get(value const &v) \
- { \
- type res=static_cast<type>(v.number()); \
- if(res!=v.number()) \
- throw bad_value_cast(); \
- return res; \
- } \
- static void set(value &v,type const &in) \
- { \
- if(std::numeric_limits<type>::digits > \
- std::numeric_limits<double>::digits \
- && static_cast<double>(in)!=in) \
- { \
- throw bad_value_cast(); \
- } \
- v.number(static_cast<double>(in)); \
- } \
- };
-
- CPPCMS_JSON_SPECIALIZE_INT(char)
- CPPCMS_JSON_SPECIALIZE_INT(unsigned char)
- CPPCMS_JSON_SPECIALIZE_INT(signed char)
- CPPCMS_JSON_SPECIALIZE_INT(wchar_t)
- CPPCMS_JSON_SPECIALIZE_INT(short)
- CPPCMS_JSON_SPECIALIZE_INT(unsigned short)
- CPPCMS_JSON_SPECIALIZE_INT(int)
- CPPCMS_JSON_SPECIALIZE_INT(unsigned int)
- CPPCMS_JSON_SPECIALIZE_INT(long)
- CPPCMS_JSON_SPECIALIZE_INT(unsigned long)
- CPPCMS_JSON_SPECIALIZE_INT(long long)
- CPPCMS_JSON_SPECIALIZE_INT(unsigned long long)
-
- #undef CPPCMS_JSON_SPECIALIZE_INT
-
- template<>
- struct traits<float> {
- static float get(value const &v)
- {
- double r=v.number();
- if( r < (-std::numeric_limits<float>::max()) // actually should be C++11 lowest, but it should be under IEEE float lowest()=-max()
- || std::numeric_limits<float>::max() < r )
- {
- throw bad_value_cast();
- }
- return static_cast<float>(r);
- }
- static void set(value &v,float const &in)
- {
- v.number(in);
- }
- };
-
- template<>
- struct traits<long double> {
- static long double get(value const &v)
- {
- return v.number();
- }
- static void set(value &v,long double const &in)
- {
- if( in < -std::numeric_limits<double>::max() // should actually be std::numeric_limits<float>::lowest() but it is ==-max()
- || std::numeric_limits<double>::max() < in )
- {
- throw bad_value_cast();
- }
- v.number(static_cast<double>(in));
- }
- };
-
- template<>
- struct traits<json::null> {
- static void set(value &v,json::null const &/*in*/)
- {
- v.null();
- }
- };
-
- template<int n>
- struct traits<char[n]> {
- typedef char vtype[n];
- static void set(value &v,vtype const &in)
- {
- v.str(in);
- }
- };
- template<int n>
- struct traits<char const [n]> {
- typedef char const vtype[n];
- static void set(value &v,vtype const &in)
- {
- v.str(in);
- }
- };
-
-
- template<>
- struct traits<char const *> {
- static void set(value &v,char const * const &in)
- {
- v.str(in);
- }
- };
-
- /// \endcond
-
-
- } // json
- } // cppcms
-
- #endif
|