diff --git a/include/jsonxx.h b/include/jsonxx.h index f70e369..bec291e 100644 --- a/include/jsonxx.h +++ b/include/jsonxx.h @@ -5,707 +5,600 @@ // Sean Middleditch // rlyeh -#pragma once +#ifndef JSONXX_DEFINE_H +#define JSONXX_DEFINE_H #include #include #include +#include #include #include #include +#include #include // jsonxx versioning: major.minor-extra where // major = { number } // minor = { number } // extra = { 'a':alpha, 'b':beta, 'rc': release candidate, 'r': release, 's':stable } -#define JSONXX_MAJOR "0" -#define JSONXX_MINOR "22" -#define JSONXX_EXTRA "a" -#define JSONXX_VERSION JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA -#define JSONXX_XML_TAG "" +#define JSONXX_MAJOR "0" +#define JSONXX_MINOR "22" +#define JSONXX_EXTRA "a" +#define JSONXX_VERSION JSONXX_MAJOR "." JSONXX_MINOR "-" JSONXX_EXTRA +#define JSONXX_XML_TAG "" #if __cplusplus > 199711L -# define JSONXX_COMPILER_HAS_CXX11 1 +#define JSONXX_COMPILER_HAS_CXX11 1 #elif defined(_MSC_VER) && _MSC_VER > 1700 -# define JSONXX_COMPILER_HAS_CXX11 1 +#define JSONXX_COMPILER_HAS_CXX11 1 #else -# define JSONXX_COMPILER_HAS_CXX11 0 +#define JSONXX_COMPILER_HAS_CXX11 0 #endif -#ifdef _MSC_VER -// disable the C4127 warning if using VC, see https://stackoverflow.com/a/12042515 -# define JSONXX_ASSERT(...) \ - do \ - { \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) if (jsonxx::Assertions) __pragma(warning(pop)) \ - jsonxx::assertion(__FILE__, __LINE__, #__VA_ARGS__, bool(__VA_ARGS__)); \ - __pragma(warning(push)) __pragma(warning(disable : 4127)) \ - } while (0) __pragma(warning(pop)) -#else -# define JSONXX_ASSERT(...) \ - do \ - { \ - if (jsonxx::Assertions) \ - jsonxx::assertion(__FILE__, __LINE__, #__VA_ARGS__, bool(__VA_ARGS__)); \ - } while (0) -#endif +#define JSONXX_ASSERT(...) do { if( jsonxx::Assertions ) \ + jsonxx::assertion(__FILE__,__LINE__,#__VA_ARGS__,bool(__VA_ARGS__)); } while(0) -namespace jsonxx -{ +#ifndef JSONXX_HANDLE_INFINITY +#define JSONXX_HANDLE_INFINITY 1 +#endif -// FIXME(hjiang): Those should really be dynamic. -// Settings -enum Settings -{ - // constants - Enabled = true, - Disabled = false, - Permissive = true, - Strict = false, - // values - Parser = Permissive, // permissive or strict parsing - UnquotedKeys = Disabled, // support of unquoted keys - Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) -}; +#ifdef DEBUG +#define JSONXX_WARN(...) \ + std::cerr << "[WARN] " << __VA_ARGS__ << std::endl; +#else +#define JSONXX_WARN(...) ; +#endif -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4127) +namespace jsonxx { + + enum PrintMode { + Pretty, + Compact, + }; + + // Settings + enum Settings { + // constants + Enabled = true, + Disabled = false, + Permissive = true, + Strict = false, + // values + Parser = Permissive, // permissive or strict parsing + UnquotedKeys = Disabled, // support of unquoted keys +#if DEBUG + Assertions = Enabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) +#else + Assertions = Disabled // enabled or disabled assertions (these asserts work both in DEBUG and RELEASE builds) #endif -inline bool -parser_is_strict() -{ - return Parser == Strict; -} -inline bool -parser_is_permissive() -{ - return Parser == Permissive; -} -inline bool -unquoted_keys_are_enabled() -{ - return UnquotedKeys == Enabled; -} -#ifdef _MSC_VER -# pragma warning(pop) + }; + + // Constants for .write() and .xml() methods + enum Format { + JSON = 0, // JSON output + JSONx = 1, // XML output, JSONx format. see http://goo.gl/I3cxs + JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML + JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex + TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 + }; + + // Types + typedef long double Number; + typedef bool Boolean; + typedef std::string String; + struct Null {}; + class Value; + class Object; + class Array; + + // Range of Number. Valid numbers outside the range as considered infinite + constexpr Number MaxNumberRange = std::numeric_limits::max(); + constexpr Number MinNumberRange = -std::numeric_limits::max(); + + // JSON representation of infinite numbers + constexpr const char* InfinityRepresentation = "1e500"; + + // Default value + static const String EmptyString = ""; + static const Number Zero = 0; + static const Number MinusOne = -1; + static const Number One = 1; + static const Boolean True = true; + static const Boolean False = false; + + // Identity meta-function + template + struct identity { + typedef T type; + }; + + // Tools + bool validate( const std::string &input ); + bool validate( std::istream &input ); + std::string reformat( const std::string &input ); + std::string reformat( std::istream &input ); + std::string xml( const std::string &input, unsigned format = JSONx ); + std::string xml( std::istream &input, unsigned format = JSONx ); + + // Detail + void assertion( const char *file, int line, const char *expression, bool result ); + + // A JSON Object + class Object { + public: + Object(); + ~Object(); + + template + bool has(const std::string& key) const; + + // Always call has<>() first. If the key doesn't exist, consider + // the behavior undefined. + template + T& get(const std::string& key); + template + const T& get(const std::string& key) const; + + template + const T& get(const std::string& key, const typename identity::type& default_value) const; + + // Delete unsafe operation + template + const T& get(const std::string& key, typename identity::type&& default_value) const = delete; + + size_t size() const; + bool empty() const; + + const std::map& kv_map() const; + std::string json(PrintMode printMode = PrintMode::Compact, int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string write( unsigned format ) const; + + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::map container; + void import( const Object &other ); + void import( const std::string &key, const Value &value ); + Object &operator<<(const Value &value); + Object &operator<<(const Object &value); + Object &operator=(const Object &value); + Object(const Object &other); + Object(const std::string &key, const Value &value); + template + Object(const char (&key)[N], const Value &value) { + import(key,value); + } + template + Object &operator<<(const T &value); + + protected: + static bool parse(std::istream& input, Object& object); + container value_map_; + std::string odd; + }; + + static const Object EmptyObject = {}; + + class Array { + public: + Array(); + ~Array(); + + size_t size() const; + bool empty() const; + + template + bool has(unsigned int i) const; + + template + T& get(unsigned int i); + template + const T& get(unsigned int i) const; + + template + const T& get(unsigned int i, const typename identity::type& default_value) const; + + // Delete unsafe operation + template + const T& get(unsigned int i, typename identity::type&& default_value) const = delete; + + const std::vector& values() const { + return values_; + } + std::string json(PrintMode printMode = PrintMode::Compact, int floatPrecision = std::numeric_limits::digits10 + 1) const; + std::string xml( unsigned format = JSONx, const std::string &header = std::string(), const std::string &attrib = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) const; + + std::string write( unsigned format ) const { return format == JSON ? json() : xml(format); } + void reset(); + bool parse(std::istream &input); + bool parse(const std::string &input); + typedef std::vector container; + void import(const Array &other); + void import(const Value &value); + Array &operator<<(const Array &other); + Array &operator<<(const Value &value); + Array &operator=(const Array &other); + Array &operator=(const Value &value); + Array(const Array &other); + Array(const Value &value); + protected: + static bool parse(std::istream& input, Array& array); + container values_; + }; + + static const Array EmptyArray = {}; + + // A value could be a number, an array, a string, an object, a + // boolean, or null + class Value { + public: + + Value(); + ~Value() { reset(); } + void reset(); + +#ifdef JSONXX_ALLOW_INVALID_TYPES + template + [[deprecated("this type is not natively supported by jsonxx, therefore its value will be converted to 'null'")]] + void import( const T& t ) { + reset(); + type_ = INVALID_; + JSONXX_WARN( "No JSONXX support for " << typeid(t).name() ); + } +#else + template void import( const T& t ) = delete; #endif -// Constants for .write() and .xml() methods -enum Format -{ - JSON = 0, // JSON output - JSONx = 1, // XML output, JSONx format. see https://goo.gl/I3cxs - JXML = 2, // XML output, JXML format. see https://github.com/r-lyeh/JXML - JXMLex = 3, // XML output, JXMLex format. see https://github.com/r-lyeh/JXMLex - TaggedXML = 4 // XML output, tagged XML format. see https://github.com/hjiang/jsonxx/issues/12 -}; - -// Types -typedef long double Number; -typedef bool Boolean; -typedef std::string String; -struct Null -{}; -class Value; -class Object; -class Array; - -// Identity meta-function -template -struct identity -{ - typedef T type; -}; - -// Tools -bool -validate(const std::string & input); -bool -validate(std::istream & input); -std::string -reformat(const std::string & input); -std::string -reformat(std::istream & input); -std::string -xml(const std::string & input, unsigned format = JSONx); -std::string -xml(std::istream & input, unsigned format = JSONx); - -// Detail -void -assertion(const char * file, int line, const char * expression, bool result); - -// A JSON Object -class Object -{ -public: - Object(); - ~Object(); - - template - bool - has(const std::string & key) const; - - // Always call has<>() first. If the key doesn't exist, consider - // the behavior undefined. - template - T & - get(const std::string & key); - template - const T & - get(const std::string & key) const; - - template - const T & - get(const std::string & key, const typename identity::type & default_value) const; - - size_t - size() const; - bool - empty() const; - - const std::map & - kv_map() const; - std::string - json() const; - std::string - xml(unsigned format = JSONx, - const std::string & header = std::string(), - const std::string & attrib = std::string()) const; - std::string - write(unsigned format) const; - - void - reset(); - bool - parse(std::istream & input); - bool - parse(const std::string & input); - typedef std::map container; - void - import(const Object & other); - void - import(const std::string & key, const Value & value); - Object & - operator<<(const Value & value); - Object & - operator<<(const Object & value); - Object & - operator=(const Object & value); - Object(const Object & other); - Object(const std::string & key, const Value & value); - template - Object(const char (&key)[N], const Value & value) - { - import(key, value); - } - template - Object & - operator<<(const T & value); - -protected: - static bool - parse(std::istream & input, Object & object); - container value_map_; - std::string odd; -}; - -class Array -{ -public: - Array(); - ~Array(); - - size_t - size() const; - bool - empty() const; - - template - bool - has(unsigned int i) const; - - template - T & - get(unsigned int i); - template - const T & - get(unsigned int i) const; - - template - const T & - get(unsigned int i, const typename identity::type & default_value) const; - - const std::vector & - values() const - { - return values_; - } - std::string - json() const; - std::string - xml(unsigned format = JSONx, - const std::string & header = std::string(), - const std::string & attrib = std::string()) const; - - std::string - write(unsigned format) const - { - return format == JSON ? json() : xml(format); - } - void - reset(); - bool - parse(std::istream & input); - bool - parse(const std::string & input); - typedef std::vector container; - void - append(const Array & other); - void - append(const Value & value) - { - import(value); - } - void - import(const Array & other); - void - import(const Value & value); - Array & - operator<<(const Array & other); - Array & - operator<<(const Value & value); - Array & - operator=(const Array & other); - Array & - operator=(const Value & value); - Array(const Array & other); - Array(const Value & value); - -protected: - static bool - parse(std::istream & input, Array & array); - container values_; -}; - -// A value could be a number, an array, a string, an object, a -// boolean, or null -class Value -{ -public: - Value(); - ~Value() { reset(); } - void - reset(); - - template - void - import(const T &) - { - reset(); - type_ = INVALID_; - // debug - // std::cout << "[WARN] No support for " << typeid(t).name() << std::endl; - } - void - import(const bool & b) - { - reset(); - type_ = BOOL_; - bool_value_ = b; - } -#define local_number(TYPE) \ - void import(const TYPE & n) \ - { \ - reset(); \ - type_ = NUMBER_; \ - number_value_ = static_cast(n); \ - } - local_number(char) local_number(int) local_number(long) local_number(long long) local_number(unsigned char) - local_number(unsigned int) local_number(unsigned long) local_number(unsigned long long) local_number(float) - local_number(double) local_number(long double) -#undef local_number + void import( const bool &b ) { + reset(); + type_ = BOOL_; + bool_value_ = b; + } +#define $number(TYPE) \ +void import( const TYPE &n ) { \ +reset(); \ +type_ = NUMBER_; \ +number_value_ = static_cast(n); \ +} + $number( char ) + $number( int ) + $number( long ) + $number( long long ) + $number( unsigned char ) + $number( unsigned int ) + $number( unsigned long ) + $number( unsigned long long ) + $number( float ) + $number( double ) + $number( long double ) +#undef $number #if JSONXX_COMPILER_HAS_CXX11 > 0 - void import(const std::nullptr_t &) - { - reset(); - type_ = NULL_; - } + void import( const std::nullptr_t & ) { + reset(); + type_ = NULL_; + } +#endif + void import( const Null & ) { + reset(); + type_ = NULL_; + } + void import( const String &s ) { + reset(); + type_ = STRING_; + *( string_value_ = new String() ) = s; + } + void import( const std::string_view &sv ) { + reset(); + type_ = STRING_; + *( string_value_ = new String() ) = sv; + } + template + void import( const std::vector &array ) { + Array a; + for (const auto& elmt : array) { + a << elmt; + } + import(a); + } + void import( const Array &a ) { + reset(); + type_ = ARRAY_; + *( array_value_ = new Array() ) = a; + } + void import( const Object &o ) { + reset(); + type_ = OBJECT_; + *( object_value_ = new Object() ) = o; + } + void import( const Value &other ) { + if (this != &other) + switch (other.type_) { + case NULL_: + import( Null() ); + break; + case BOOL_: + import( other.bool_value_ ); + break; + case NUMBER_: + import( other.number_value_ ); + break; + case STRING_: + import( *other.string_value_ ); + break; + case ARRAY_: + import( *other.array_value_ ); + break; + case OBJECT_: + import( *other.object_value_ ); + break; + case INVALID_: + type_ = INVALID_; + break; + default: + JSONXX_ASSERT( !"not implemented" ); + } + } + template + Value &operator <<( const T &t ) { + import(t); + return *this; + } + template + Value &operator =( const T &t ) { + reset(); + import(t); + return *this; + } + Value(const Value &other); + +#define $Value(TYPE) Value( const TYPE &t ) : type_(INVALID_) { import(t); } + +#ifdef JSONXX_ALLOW_INVALID_TYPES + template $Value( T ) +#else + $Value( bool ) + $Value( char ) + $Value( int ) + $Value( long ) + $Value( long long ) + $Value( unsigned char ) + $Value( unsigned int ) + $Value( unsigned long ) + $Value( unsigned long long ) + $Value( float ) + $Value( double ) + $Value( long double ) +#if JSONXX_COMPILER_HAS_CXX11 > 0 + $Value( std::nullptr_t ) +#endif + $Value( Null ) + $Value( String ) + $Value( std::string_view ) + template $Value( std::vector ) + $Value( Array ) + $Value( Object ) #endif - void - import(const Null &) - { - reset(); - type_ = NULL_; - } - void - import(const String & s) - { - reset(); - type_ = STRING_; - *(string_value_ = new String()) = s; - } - void - import(const Array & a) - { - reset(); - type_ = ARRAY_; - *(array_value_ = new Array()) = a; - } - void - import(const Object & o) - { - reset(); - type_ = OBJECT_; - *(object_value_ = new Object()) = o; - } - void - import(const Value & other) - { - if (this != &other) - switch (other.type_) - { - case NULL_: - import(Null()); - break; - case BOOL_: - import(other.bool_value_); - break; - case NUMBER_: - import(other.number_value_); - break; - case STRING_: - import(*other.string_value_); - break; - case ARRAY_: - import(*other.array_value_); - break; - case OBJECT_: - import(*other.object_value_); - break; - case INVALID_: - type_ = INVALID_; - break; - default: - JSONXX_ASSERT(!"not implemented"); - } - } - template - Value & - operator<<(const T & t) - { - import(t); - return *this; - } - template - Value & - operator=(const T & t) - { - reset(); - import(t); - return *this; - } - Value(const Value & other); - template - Value(const T & t) - : type_(INVALID_) - { - import(t); - } - template - Value(const char (&t)[N]) - : type_(INVALID_) - { - import(std::string(t)); - } - - bool - parse(std::istream & input); - bool - parse(const std::string & input); - - template - bool - is() const; - template - T & - get(); - template - const T & - get() const; - - bool - empty() const; - -public: - enum - { - NUMBER_, - STRING_, - BOOL_, - NULL_, - ARRAY_, - OBJECT_, - INVALID_ - } type_; - union - { - Number number_value_; - String * string_value_; - Boolean bool_value_; - Array * array_value_; - Object * object_value_; - }; - -protected: - static bool - parse(std::istream & input, Value & value); -}; - -template -bool -Array::has(unsigned int i) const -{ - if (i >= size()) - { - return false; - } - else - { - Value * v = values_.at(i); - return v->is(); - } -} - -template -T & -Array::get(unsigned int i) -{ - JSONXX_ASSERT(i < size()); - Value * v = values_.at(i); - return v->get(); -} - -template -const T & -Array::get(unsigned int i) const -{ - JSONXX_ASSERT(i < size()); - const Value * v = values_.at(i); - return v->get(); -} - -template -const T & -Array::get(unsigned int i, const typename identity::type & default_value) const -{ - if (has(i)) - { - const Value * v = values_.at(i); - return v->get(); - } - else - { - return default_value; - } -} - -template -bool -Object::has(const std::string & key) const -{ - container::const_iterator it(value_map_.find(key)); - return it != value_map_.end() && it->second->is(); -} - -template -T & -Object::get(const std::string & key) -{ - JSONXX_ASSERT(has(key)); - return value_map_.find(key)->second->get(); -} - -template -const T & -Object::get(const std::string & key) const -{ - JSONXX_ASSERT(has(key)); - return value_map_.find(key)->second->get(); -} - -template -const T & -Object::get(const std::string & key, const typename identity::type & default_value) const -{ - if (has(key)) - { - return value_map_.find(key)->second->get(); - } - else - { - return default_value; - } -} - -template <> -inline bool -Value::is() const -{ - return true; -} - -template <> -inline bool -Value::is() const -{ - return type_ == NULL_; -} - -template <> -inline bool -Value::is() const -{ - return type_ == BOOL_; -} - -template <> -inline bool -Value::is() const -{ - return type_ == STRING_; -} - -template <> -inline bool -Value::is() const -{ - return type_ == NUMBER_; -} - -template <> -inline bool -Value::is() const -{ - return type_ == ARRAY_; -} - -template <> -inline bool -Value::is() const -{ - return type_ == OBJECT_; -} - -template <> -inline Value & -Value::get() -{ - return *this; -} - -template <> -inline const Value & -Value::get() const -{ - return *this; -} - -template <> -inline bool & -Value::get() -{ - JSONXX_ASSERT(is()); - return bool_value_; -} - -template <> -inline std::string & -Value::get() -{ - JSONXX_ASSERT(is()); - return *string_value_; -} - -template <> -inline Number & -Value::get() -{ - JSONXX_ASSERT(is()); - return number_value_; -} - -template <> -inline Array & -Value::get() -{ - JSONXX_ASSERT(is()); - return *array_value_; -} - -template <> -inline Object & -Value::get() -{ - JSONXX_ASSERT(is()); - return *object_value_; -} - -template <> -inline const Boolean & -Value::get() const -{ - JSONXX_ASSERT(is()); - return bool_value_; -} - -template <> -inline const String & -Value::get() const -{ - JSONXX_ASSERT(is()); - return *string_value_; -} - -template <> -inline const Number & -Value::get() const -{ - JSONXX_ASSERT(is()); - return number_value_; -} - -template <> -inline const Array & -Value::get() const -{ - JSONXX_ASSERT(is()); - return *array_value_; -} - -template <> -inline const Object & -Value::get() const -{ - JSONXX_ASSERT(is()); - return *object_value_; -} - -template -inline Object & -Object::operator<<(const T & value) -{ - *this << Value(value); - return *this; -} -} // namespace jsonxx +#undef $Value + + template + Value( const char (&t)[N] ) : type_(INVALID_) { import( std::string(t) ); } + + bool parse(std::istream &input); + bool parse(const std::string &input); + + template + bool is() const; + template + T& get(); + template + const T& get() const; + + bool empty() const; + + public: + enum { + NUMBER_, + STRING_, + BOOL_, + NULL_, + ARRAY_, + OBJECT_, + INVALID_ + } type_; + union { + Number number_value_; + String* string_value_; + Boolean bool_value_; + Array* array_value_; + Object* object_value_; + }; + + protected: + static bool parse(std::istream& input, Value& value); + }; + + static const Value EmptyValue = {}; + + template + bool Array::has(unsigned int i) const { + if (i >= size()) { + return false; + } else { + Value* v = values_.at(i); + return v->is(); + } + } + + template + T& Array::get(unsigned int i) { + JSONXX_ASSERT(i < size()); + Value* v = values_.at(i); + return v->get(); + } + + template + const T& Array::get(unsigned int i) const { + JSONXX_ASSERT(i < size()); + const Value* v = values_.at(i); + return v->get(); + } + + template + const T& Array::get(unsigned int i, const typename identity::type& default_value) const { + if(i < size()) { + const Value* v = values_.at(i); + if (v->is()) { + return v->get(); + } + } + return default_value; + } + + template + bool Object::has(const std::string& key) const { + container::const_iterator it(value_map_.find(key)); + return it != value_map_.end() && it->second->is(); + } + + template + T& Object::get(const std::string& key) { + JSONXX_ASSERT(has(key)); + return value_map_.find(key)->second->get(); + } + + template + const T& Object::get(const std::string& key) const { + JSONXX_ASSERT(has(key)); + return value_map_.find(key)->second->get(); + } + + template + const T& Object::get(const std::string& key, const typename identity::type& default_value) const { + auto iterator = value_map_.find(key); + if (iterator != value_map_.end() && iterator->second->is()) { + return iterator->second->get(); + } else { + return default_value; + } + } + + template<> + inline bool Value::is() const { + return true; + } + + template<> + inline bool Value::is() const { + return type_ == NULL_; + } + + template<> + inline bool Value::is() const { + return type_ == BOOL_; + } + + template<> + inline bool Value::is() const { + return type_ == STRING_; + } + + template<> + inline bool Value::is() const { + return type_ == NUMBER_; + } + + template<> + inline bool Value::is() const { + return type_ == ARRAY_; + } + + template<> + inline bool Value::is() const { + return type_ == OBJECT_; + } + + template<> + inline Value& Value::get() { + return *this; + } + + template<> + inline const Value& Value::get() const { + return *this; + } + + template<> + inline bool& Value::get() { + JSONXX_ASSERT(is()); + return bool_value_; + } + + template<> + inline std::string& Value::get() { + JSONXX_ASSERT(is()); + return *string_value_; + } + + template<> + inline Number& Value::get() { + JSONXX_ASSERT(is()); + return number_value_; + } + + template<> + inline Array& Value::get() { + JSONXX_ASSERT(is()); + return *array_value_; + } + + template<> + inline Object& Value::get() { + JSONXX_ASSERT(is()); + return *object_value_; + } + + template<> + inline const Boolean& Value::get() const { + JSONXX_ASSERT(is()); + return bool_value_; + } + + template<> + inline const String& Value::get() const { + JSONXX_ASSERT(is()); + return *string_value_; + } + + template<> + inline const Number& Value::get() const { + JSONXX_ASSERT(is()); + return number_value_; + } + + template<> + inline const Array& Value::get() const { + JSONXX_ASSERT(is()); + return *array_value_; + } + + template<> + inline const Object& Value::get() const { + JSONXX_ASSERT(is()); + return *object_value_; + } + + template + inline Object &Object::operator<<(const T &value) { + *this << Value(value); + return *this; + } +} // namespace jsonxx + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v); +std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v); +std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v); -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Value & v); -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Object & v); -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Array & v); +#endif diff --git a/src/jsonxx.cc b/src/jsonxx.cc index f259e95..7fec9ef 100644 --- a/src/jsonxx.cc +++ b/src/jsonxx.cc @@ -14,1466 +14,1219 @@ #include #include #include +#include // Snippet that creates an assertion function that works both in DEBUG & RELEASE mode. // JSONXX_ASSERT(...) macro will redirect to this. assert() macro is kept untouched. #if defined(NDEBUG) || defined(_NDEBUG) -# define JSONXX_REENABLE_NDEBUG -# undef NDEBUG -# undef _NDEBUG +# define JSONXX_REENABLE_NDEBUG +# undef NDEBUG +# undef _NDEBUG #endif #include #include -void -jsonxx::assertion(const char * file, int line, const char * expression, bool result) -{ - if (!result) - { - fprintf(stderr, "[JSONXX] expression '%s' failed at %s:%d -> ", expression, file, line); - assert(0); - } +void jsonxx::assertion( const char *file, int line, const char *expression, bool result ) { + if( !result ) { + fprintf( stderr, "[JSONXX] expression '%s' failed at %s:%d -> ", expression, file, line ); + assert( 0 ); + } } #if defined(JSONXX_REENABLE_NDEBUG) -# define NDEBUG -# define _NDEBUG +# define NDEBUG +# define _NDEBUG #endif #include -namespace jsonxx -{ - -// static_assert( sizeof(unsigned long long) < sizeof(long double), "'long double' cannot hold 64bit values in this -// compiler :("); - -bool -match(const char * pattern, std::istream & input); -bool -parse_array(std::istream & input, Array & array); -bool -parse_bool(std::istream & input, Boolean & value); -bool -parse_comment(std::istream & input); -bool -parse_null(std::istream & input); -bool -parse_number(std::istream & input, Number & value); -bool -parse_object(std::istream & input, Object & object); -bool -parse_string(std::istream & input, String & value); -bool -parse_identifier(std::istream & input, String & value); -bool -parse_value(std::istream & input, Value & value); +namespace jsonxx { + +//static_assert( sizeof(unsigned long long) < sizeof(long double), "'long double' cannot hold 64bit values in this compiler :("); + +bool match(const char* pattern, std::istream& input); +bool parse_array(std::istream& input, Array& array); +bool parse_bool(std::istream& input, Boolean& value); +bool parse_comment(std::istream &input); +bool parse_null(std::istream& input); +bool parse_number(std::istream& input, Number& value); +bool parse_number_inf(std::istream& input, Number& value, std::streampos rollback); +bool parse_object(std::istream& input, Object& object); +bool parse_string(std::istream& input, String& value); +bool parse_identifier(std::istream& input, String& value); +bool parse_value(std::istream& input, Value& value); // Try to consume characters from the input stream and match the // pattern string. -bool -match(const char * pattern, std::istream & input) -{ - input >> std::ws; - const char * cur(pattern); - char ch(0); - while (input && !input.eof() && *cur != 0) - { - input.get(ch); - if (ch != *cur) - { - input.putback(ch); - if (parse_comment(input)) - continue; - while (cur > pattern) - { - cur--; - input.putback(*cur); - } - return false; - } - else - { - cur++; +bool match(const char* pattern, std::istream& input) { + input >> std::ws; + const char* cur(pattern); + char ch(0); + while(input && !input.eof() && *cur != 0) { + input.get(ch); + if (ch != *cur) { + input.putback(ch); + if( parse_comment(input) ) + continue; + while (cur > pattern) { + cur--; + input.putback(*cur); + } + return false; + } else { + cur++; + } } - } - return *cur == 0; + return *cur == 0; } -bool -parse_string(std::istream & input, String & value) -{ - char ch = '\0', delimiter = '"'; - if (!match("\"", input)) - { - if (parser_is_strict()) - { - return false; - } - delimiter = '\''; - if (input.peek() != delimiter) - { - return false; - } - input.get(ch); - } - while (!input.eof() && input.good()) - { - input.get(ch); - if (ch == delimiter) - { - break; +bool parse_string(std::istream& input, String& value) { + char ch = '\0', delimiter = '"'; + if (!match("\"", input)) { + if (Parser == Strict) { + return false; + } + delimiter = '\''; + if (input.peek() != delimiter) { + return false; + } + input.get(ch); } - if (ch == '\\') - { - input.get(ch); - switch (ch) - { - case '\\': - case '/': - value.push_back(ch); - break; - case 'b': - value.push_back('\b'); - break; - case 'f': - value.push_back('\f'); - break; - case 'n': - value.push_back('\n'); - break; - case 'r': - value.push_back('\r'); - break; - case 't': - value.push_back('\t'); - break; - case 'u': - { - int i; - std::stringstream ss; - for (i = 0; (!input.eof() && input.good()) && i < 4; ++i) - { - input.get(ch); - ss << std::hex << ch; - } - if (input.good() && (ss >> i)) - value.push_back(static_cast(i)); + while(!input.eof() && input.good()) { + input.get(ch); + if (ch == delimiter) { + break; } - break; - default: - if (ch != delimiter) - { - value.push_back('\\'); - value.push_back(ch); - } - else + if (ch == '\\') { + input.get(ch); + switch(ch) { + case '\\': + case '/': + value.push_back(ch); + break; + case 'b': + value.push_back('\b'); + break; + case 'f': + value.push_back('\f'); + break; + case 'n': + value.push_back('\n'); + break; + case 'r': + value.push_back('\r'); + break; + case 't': + value.push_back('\t'); + break; + case 'u': { + int i; + std::stringstream ss; + for( i = 0; (!input.eof() && input.good()) && i < 4; ++i ) { + input.get(ch); + ss << std::hex << ch; + } + if( input.good() && (ss >> i) ) + value.push_back(i); + } + break; + default: + if (ch != delimiter) { + value.push_back('\\'); + value.push_back(ch); + } else value.push_back(ch); + break; + } + } else { value.push_back(ch); - break; - } + } } - else - { - value.push_back(ch); + if (input && ch == delimiter) { + return true; + } else { + return false; } - } - if (input && ch == delimiter) - { - return true; - } - else - { - return false; - } } -bool -parse_identifier(std::istream & input, String & value) -{ - input >> std::ws; +bool parse_identifier(std::istream& input, String& value) { + input >> std::ws; - char ch = '\0', delimiter = ':'; - bool first = true; + char ch = '\0', delimiter = ':'; + bool first = true; - while (!input.eof() && input.good()) - { - input.get(ch); + while(!input.eof() && input.good()) { + input.get(ch); - if (ch == delimiter) - { - input.unget(); - break; + if (ch == delimiter) { + input.unget(); + break; + } + + if(first) { + if ((ch != '_' && ch != '$') && + (ch < 'a' || ch > 'z') && + (ch < 'A' || ch > 'Z')) { + return false; + } + first = false; + } + if(ch == '_' || ch == '$' || + (ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9')) { + value.push_back(ch); + } + else if(ch == '\t' || ch == ' ') { + input >> std::ws; + } + } + if (input && ch == delimiter) { + return true; + } else { + return false; } +} - if (first) - { - if ((ch != '_' && ch != '$') && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) - { +bool parse_number(std::istream& input, Number& value) { + input >> std::ws; + std::streampos rollback = input.tellg(); + input >> value; + if (input.fail()) { + if (parse_number_inf(input, value, rollback)) { + return true; + } + + input.clear(); + input.seekg(rollback); return false; - } - first = false; } - if (ch == '_' || ch == '$' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) - { - value.push_back(ch); + +#if JSONXX_HANDLE_INFINITY + if (value >= MaxNumberRange) { + value = std::numeric_limits::infinity(); } - else if (ch == '\t' || ch == ' ') - { - input >> std::ws; + else if (value <= MinNumberRange) { + value = -std::numeric_limits::infinity(); } - } - if (input && ch == delimiter) - { +#endif return true; - } - else - { - return false; - } } -bool -parse_number(std::istream & input, Number & value) -{ - input >> std::ws; - std::streampos rollback = input.tellg(); - input >> value; - if (input.fail()) - { +bool parse_number_inf(std::istream& input, Number& value, std::streampos rollback) { input.clear(); input.seekg(rollback); - return false; - } - return true; -} -bool -parse_bool(std::istream & input, Boolean & value) -{ - if (match("true", input)) - { - value = true; - return true; - } - if (match("false", input)) - { - value = false; - return true; - } - return false; + switch (input.peek()) { + case '-': + input.get(); + value = -std::numeric_limits::infinity(); + break; + case '+': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + input.get(); + value = std::numeric_limits::infinity(); + break; + default: + return false; + } + + int ch; + do { + ch = input.get(); + } while (isdigit(ch)); + + if (ch != 'E' && ch != 'e') { + return false; + } + + int exponent; + input >> exponent; + return !input.fail(); } -bool -parse_null(std::istream & input) -{ - if (match("null", input)) - { - return true; - } - if (parser_is_strict()) - { +bool parse_bool(std::istream& input, Boolean& value) { + if (match("true", input)) { + value = true; + return true; + } + if (match("false", input)) { + value = false; + return true; + } return false; - } - return (input.peek() == ','); } -bool -parse_array(std::istream & input, Array & array) -{ - return array.parse(input); +bool parse_null(std::istream& input) { + if (match("null", input)) { + return true; + } + if (Parser == Strict) { + return false; + } + return (input.peek()==','); } -bool -parse_object(std::istream & input, Object & object) -{ - return object.parse(input); +bool parse_array(std::istream& input, Array& array) { + return array.parse(input); } -bool -parse_comment(std::istream & input) -{ - if (parser_is_permissive()) - if (!input.eof() && input.peek() == '/') - { - char ch0(0); - input.get(ch0); +bool parse_object(std::istream& input, Object& object) { + return object.parse(input); +} - if (!input.eof()) - { - char ch1(0); - input.get(ch1); +bool parse_comment(std::istream &input) { + if( Parser == Permissive ) + if( !input.eof() && input.peek() == '/' ) + { + char ch0(0); + input.get(ch0); - if (ch0 == '/' && ch1 == '/') + if( !input.eof() ) { - // trim chars till \r or \n - for (char ch(0); !input.eof() && (input.peek() != '\r' && input.peek() != '\n');) - input.get(ch); - - // consume spaces, tabs, \r or \n, in case no eof is found - if (!input.eof()) - input >> std::ws; - return true; + char ch1(0); + input.get(ch1); + + if( ch0 == '/' && ch1 == '/' ) + { + // trim chars till \r or \n + for( char ch(0); !input.eof() && (input.peek() != '\r' && input.peek() != '\n'); ) + input.get(ch); + + // consume spaces, tabs, \r or \n, in case no eof is found + if( !input.eof() ) + input >> std::ws; + return true; + } + + input.unget(); + input.clear(); } input.unget(); input.clear(); - } - - input.unget(); - input.clear(); } - return false; + return false; } -bool -parse_value(std::istream & input, Value & value) -{ - return value.parse(input); +bool parse_value(std::istream& input, Value& value) { + return value.parse(input); } -Object::Object() - : value_map_() -{} +Object::Object() : value_map_() {} -Object::~Object() -{ - reset(); +Object::~Object() { + reset(); } -bool -Object::parse(std::istream & input, Object & object) -{ - object.reset(); - - if (!match("{", input)) - { - return false; - } - if (match("}", input)) - { - return true; - } +bool Object::parse(std::istream& input, Object& object) { + object.reset(); - do - { - std::string key; - if (unquoted_keys_are_enabled()) - { - if (!parse_identifier(input, key)) - { - if (parser_is_permissive()) - { - if (input.peek() == '}') - break; - } + if (!match("{", input)) { return false; - } } - else - { - if (!parse_string(input, key)) - { - if (parser_is_permissive()) - { - if (input.peek() == '}') + if (match("}", input)) { + return true; + } + + do { + std::string key; + if(UnquotedKeys == Enabled) { + if (!parse_identifier(input, key)) { + if (Parser == Permissive) { + if (input.peek() == '}') + break; + } + return false; + } + } + else { + if (!parse_string(input, key)) { + if (Parser == Permissive) { + if (input.peek() == '}') + break; + } + return false; + } + } + if (!match(":", input)) { + return false; + } + Value* v = new Value(); + if (!parse_value(input, *v)) { + delete v; break; } - return false; - } - } - if (!match(":", input)) - { - return false; - } - Value * v = new Value(); - if (!parse_value(input, *v)) - { - delete v; - break; - } - // TODO(hjiang): Add an option to allow duplicated keys? - if (object.value_map_.find(key) == object.value_map_.end()) - { - object.value_map_[key] = v; - } - else - { - if (parser_is_permissive()) - { - delete object.value_map_[key]; object.value_map_[key] = v; - } - else - { - delete v; - return false; - } - } - } while (match(",", input)); + } while (match(",", input)); - if (!match("}", input)) - { - return false; - } + if (!match("}", input)) { + return false; + } - return true; + return true; } -Value::Value() - : type_(INVALID_) -{} +Value::Value() : type_(INVALID_) {} -void -Value::reset() -{ - if (type_ == STRING_) - { - delete string_value_; - string_value_ = 0; - } - else if (type_ == OBJECT_) - { - delete object_value_; - object_value_ = 0; - } - else if (type_ == ARRAY_) - { - delete array_value_; - array_value_ = 0; - } +void Value::reset() { + if (type_ == STRING_) { + delete string_value_; + string_value_ = 0; + } + else if (type_ == OBJECT_) { + delete object_value_; + object_value_ = 0; + } + else if (type_ == ARRAY_) { + delete array_value_; + array_value_ = 0; + } } -bool -Value::parse(std::istream & input, Value & value) -{ - value.reset(); +bool Value::parse(std::istream& input, Value& value) { + value.reset(); - std::string string_value; - if (parse_string(input, string_value)) - { - value.string_value_ = new std::string(); - value.string_value_->swap(string_value); - value.type_ = STRING_; - return true; - } - if (parse_number(input, value.number_value_)) - { - value.type_ = NUMBER_; - return true; - } + std::string string_value; + if (parse_string(input, string_value)) { + value.string_value_ = new std::string(); + value.string_value_->swap(string_value); + value.type_ = STRING_; + return true; + } + if (parse_number(input, value.number_value_)) { + value.type_ = NUMBER_; + return true; + } - if (parse_bool(input, value.bool_value_)) - { - value.type_ = BOOL_; - return true; - } - if (parse_null(input)) - { - value.type_ = NULL_; - return true; - } - if (input.peek() == '[') - { - value.array_value_ = new Array(); - if (parse_array(input, *value.array_value_)) - { - value.type_ = ARRAY_; - return true; + if (parse_bool(input, value.bool_value_)) { + value.type_ = BOOL_; + return true; } - delete value.array_value_; - value.array_value_ = 0; - } - value.object_value_ = new Object(); - if (parse_object(input, *value.object_value_)) - { - value.type_ = OBJECT_; - return true; - } - delete value.object_value_; - value.object_value_ = 0; - return false; + if (parse_null(input)) { + value.type_ = NULL_; + return true; + } + if (input.peek() == '[') { + value.array_value_ = new Array(); + if (parse_array(input, *value.array_value_)) { + value.type_ = ARRAY_; + return true; + } + delete value.array_value_; + } + value.object_value_ = new Object(); + if (parse_object(input, *value.object_value_)) { + value.type_ = OBJECT_; + return true; + } + delete value.object_value_; + return false; } -Array::Array() - : values_() -{} +Array::Array() : values_() {} -Array::~Array() -{ - reset(); +Array::~Array() { + reset(); } -bool -Array::parse(std::istream & input, Array & array) -{ - array.reset(); - - if (!match("[", input)) - { - return false; - } - if (match("]", input)) - { - return true; - } +bool Array::parse(std::istream& input, Array& array) { + array.reset(); - do - { - Value * v = new Value(); - if (!parse_value(input, *v)) - { - delete v; - break; + if (!match("[", input)) { + return false; + } + if (match("]", input)) { + return true; } - array.values_.push_back(v); - } while (match(",", input)); - - if (!match("]", input)) - { - return false; - } - return true; -} -static std::ostream & -stream_string(std::ostream & stream, const std::string & string) -{ - stream << '"'; - for (std::string::const_iterator i = string.begin(), e = string.end(); i != e; ++i) - { - switch (*i) - { - case '"': - stream << "\\\""; - break; - case '\\': - stream << "\\\\"; - break; - case '/': - stream << "\\/"; - break; - case '\b': - stream << "\\b"; - break; - case '\f': - stream << "\\f"; - break; - case '\n': - stream << "\\n"; - break; - case '\r': - stream << "\\r"; - break; - case '\t': - stream << "\\t"; - break; - default: - if (*i < 32) - { - stream << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast(*i) << std::dec - << std::setw(0); - } - else - { - stream << *i; + do { + Value* v = new Value(); + if (!parse_value(input, *v)) { + delete v; + break; } + array.values_.push_back(v); + } while (match(",", input)); + + if (!match("]", input)) { + return false; } - } - stream << '"'; - return stream; + return true; } -} // namespace jsonxx - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Value & v) -{ - using namespace jsonxx; - if (v.is()) - { - return stream << v.get(); - } - else if (v.is()) - { - return stream_string(stream, v.get()); - } - else if (v.is()) - { - if (v.get()) - { - return stream << "true"; +static std::ostream& stream_string(std::ostream& stream, + const std::string& string) { + stream << '"'; + for (std::string::const_iterator i = string.begin(), + e = string.end(); i != e; ++i) { + switch (*i) { + case '"': + stream << "\\\""; + break; + case '\\': + stream << "\\\\"; + break; + case '/': + stream << "\\/"; + break; + case '\b': + stream << "\\b"; + break; + case '\f': + stream << "\\f"; + break; + case '\n': + stream << "\\n"; + break; + case '\r': + stream << "\\r"; + break; + case '\t': + stream << "\\t"; + break; + default: + if (*i < 32) { + stream << "\\u" << std::hex << std::setw(4) << + std::setfill('0') << static_cast(*i) << std::dec << + std::setw(0); + } else { + stream << *i; + } + } } - else - { - return stream << "false"; + stream << '"'; + return stream; +} + +} // namespace jsonxx + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Value& v) { + using namespace jsonxx; + if (v.is()) { + return stream << v.get(); + } else if (v.is()) { + return stream_string(stream, v.get()); + } else if (v.is()) { + if (v.get()) { + return stream << "true"; + } else { + return stream << "false"; + } + } else if (v.is()) { + return stream << "null"; + } else if (v.is()) { + return stream << v.get(); + } else if (v.is()){ + return stream << v.get(); } - } - else if (v.is()) - { - return stream << "null"; - } - else if (v.is()) - { - return stream << v.get(); - } - else if (v.is()) - { - return stream << v.get(); - } - // Shouldn't reach here. - return stream; -} - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Array & v) -{ - stream << "["; - jsonxx::Array::container::const_iterator it = v.values().begin(), end = v.values().end(); - while (it != end) - { - stream << *(*it); - ++it; - if (it != end) - { - stream << ", "; + // Shouldn't reach here. + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Array& v) { + stream << "["; + jsonxx::Array::container::const_iterator + it = v.values().begin(), + end = v.values().end(); + while (it != end) { + stream << *(*it); + ++it; + if (it != end) { + stream << ", "; + } } - } - return stream << "]"; -} - -std::ostream & -operator<<(std::ostream & stream, const jsonxx::Object & v) -{ - stream << "{"; - jsonxx::Object::container::const_iterator it = v.kv_map().begin(), end = v.kv_map().end(); - while (it != end) - { - jsonxx::stream_string(stream, it->first); - stream << ": " << *(it->second); - ++it; - if (it != end) - { - stream << ", "; + return stream << "]"; +} + +std::ostream& operator<<(std::ostream& stream, const jsonxx::Object& v) { + stream << "{"; + jsonxx::Object::container::const_iterator + it = v.kv_map().begin(), + end = v.kv_map().end(); + while (it != end) { + jsonxx::stream_string(stream, it->first); + stream << ": " << *(it->second); + ++it; + if (it != end) { + stream << ", "; + } } - } - return stream << "}"; + return stream << "}"; } -namespace jsonxx -{ -namespace -{ +namespace jsonxx { +namespace { typedef unsigned char byte; -// template -std::string -escape_string(const std::string & input, const bool quote = false) -{ - static std::string map[256], *once = 0; - static std::mutex mutex; - if (!once) - { - // The problem here is that, once is initializing at the end of job, but if multithreaded application is starting - // this for the first time it will jump into this part with several threads and cause double free or corruptions. To - // prevent it, it is required to have mutex, to lock other threads while only one of them is constructing the static - // map. - mutex.lock(); - if (!once) - { - // base - for (int i = 0; i < 256; ++i) - { - map[i] = std::string() + char(i); - } - // non-printable - for (int i = 0; i < 32; ++i) - { - std::stringstream str; - str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; - map[i] = str.str(); - } - // exceptions - map[byte('"')] = "\\\""; - map[byte('\\')] = "\\\\"; - map[byte('/')] = "\\/"; - map[byte('\b')] = "\\b"; - map[byte('\f')] = "\\f"; - map[byte('\n')] = "\\n"; - map[byte('\r')] = "\\r"; - map[byte('\t')] = "\\t"; - - once = map; +//template +std::string escape_string( const std::string &input, const bool quote = false ) { + static std::string map[256]; + static std::once_flag once; + std::call_once(once, []{ + // base + for( int i = 0; i < 256; ++i ) { + map[ i ] = std::string() + char(i); + } + // non-printable + for( int i = 0; i < 32; ++i ) { + std::stringstream str; + str << "\\u" << std::hex << std::setw(4) << std::setfill('0') << i; + map[ i ] = str.str(); + } + // exceptions + map[ byte('"') ] = "\\\""; + map[ byte('\\') ] = "\\\\"; + map[ byte('/') ] = "\\/"; + map[ byte('\b') ] = "\\b"; + map[ byte('\f') ] = "\\f"; + map[ byte('\n') ] = "\\n"; + map[ byte('\r') ] = "\\r"; + map[ byte('\t') ] = "\\t"; + }); + + std::string output; + output.reserve( input.size() * 2 + 2 ); // worst scenario + if( quote ) output += '"'; + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + if( quote ) output += '"'; + return output; +} + + +namespace json { + + std::string remove_last_comma( const std::string &_input ) { + std::string input( _input ); + size_t size = input.size(); + if( size > 2 ) { + if( input[ size - 2 ] == ',' ) + input[ size - 2 ] = ' '; + if( input[ size - 1 ] == ',' ) // compact case + input.resize(size - 1); + } + return input; } - mutex.unlock(); - } - std::string output; - output.reserve(input.size() * 2 + 2); // worst scenario - if (quote) - output += '"'; - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - if (quote) - output += '"'; - return output; -} - - -namespace json -{ - -std::string -remove_last_comma(const std::string & _input) -{ - std::string input(_input); - size_t size = input.size(); - if (size > 2) - if (input[size - 2] == ',') - input[size - 2] = ' '; - return input; -} - -std::string -tag(unsigned format, unsigned depth, const std::string & name, const jsonxx::Value & t) -{ - std::stringstream ss; - const std::string tab(depth, '\t'); - - if (!name.empty()) - ss << tab << '\"' << escape_string(name) << '\"' << ':' << ' '; - else - ss << tab; - - switch (t.type_) - { - default: - case jsonxx::Value::NULL_: - ss << "null"; - return ss.str() + ",\n"; - - case jsonxx::Value::BOOL_: - ss << (t.bool_value_ ? "true" : "false"); - return ss.str() + ",\n"; - - case jsonxx::Value::ARRAY_: - ss << "[\n"; - for (Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); - it != end; - ++it) - ss << tag(format, depth + 1, std::string(), **it); - return remove_last_comma(ss.str()) + tab + - "]" - ",\n"; - - case jsonxx::Value::STRING_: - ss << '\"' << escape_string(*t.string_value_) << '\"'; - return ss.str() + ",\n"; - - case jsonxx::Value::OBJECT_: - ss << "{\n"; - for (Object::container::const_iterator it = t.object_value_->kv_map().begin(), - end = t.object_value_->kv_map().end(); - it != end; - ++it) - ss << tag(format, depth + 1, it->first, *it->second); - return remove_last_comma(ss.str()) + tab + - "}" - ",\n"; - - case jsonxx::Value::NUMBER_: - // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); - ss << t.number_value_; - return ss.str() + ",\n"; - } -} -} // namespace json - -namespace xml -{ - -std::string -escape_attrib(const std::string & input) -{ - static std::string map[256], *once = 0; - if (!once) - { - for (int i = 0; i < 256; ++i) - map[i] = "_"; - for (int i = int('a'); i <= int('z'); ++i) - map[i] = std::string() + char(i); - for (int i = int('A'); i <= int('Z'); ++i) - map[i] = std::string() + char(i); - for (int i = int('0'); i <= int('9'); ++i) - map[i] = std::string() + char(i); - once = map; - } - std::string output; - output.reserve(input.size()); // worst scenario - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - return output; -} - -std::string -escape_tag(const std::string & input, unsigned format) -{ - static std::string map[256], *once = 0; - if (!once) - { - for (int i = 0; i < 256; ++i) - map[i] = std::string() + char(i); - map[byte('<')] = "<"; - map[byte('>')] = ">"; - - switch (format) - { - default: - break; - - case jsonxx::JXML: - case jsonxx::JXMLex: - case jsonxx::JSONx: - case jsonxx::TaggedXML: - map[byte('&')] = "&"; - break; + + std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, PrintMode printMode, int floatPrecision = std::numeric_limits::digits10 + 1) { + std::stringstream ss; + std::string tab(depth, '\t'); + std::string newLine("\n"); + std::string space(" "); + + switch (printMode) { + case Compact: + tab = ""; + newLine = ""; + space = ""; + break; + case Pretty: + break; + } + + if( !name.empty() ) + ss << tab << '\"' << escape_string( name ) << '\"' << ':' << space; + else + ss << tab; + + switch( t.type_ ) + { + default: + case jsonxx::Value::NULL_: + ss << "null"; + return ss.str() + "," + newLine; + + case jsonxx::Value::BOOL_: + ss << ( t.bool_value_ ? "true" : "false" ); + return ss.str() + "," + newLine; + + case jsonxx::Value::ARRAY_: + ss << "[" + newLine; + for(Array::container::const_iterator it = t.array_value_->values().begin(), + end = t.array_value_->values().end(); it != end; ++it ) + ss << tag( format, depth+1, std::string(), **it, printMode, floatPrecision ); + return remove_last_comma( ss.str() ) + tab + "]" "," + newLine; + + case jsonxx::Value::STRING_: + ss << '\"' << escape_string( *t.string_value_ ) << '\"'; + return ss.str() + "," + newLine; + + case jsonxx::Value::OBJECT_: + ss << "{" + newLine; + for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), + end = t.object_value_->kv_map().end(); it != end ; ++it) + ss << tag( format, depth+1, it->first, *it->second, printMode, floatPrecision); + return remove_last_comma( ss.str() ) + tab + "}" "," + newLine; + + case jsonxx::Value::NUMBER_: + if (isfinite(t.number_value_)) { + // max precision + ss << std::setprecision(floatPrecision); + ss << t.number_value_; + } +#if JSONXX_HANDLE_INFINITY + else if (t.number_value_ > MaxNumberRange) { + ss << InfinityRepresentation; + } + else if (t.number_value_ < MinNumberRange) { + ss << '-' << InfinityRepresentation; + } +#endif + else { + JSONXX_WARN( "No JSONXX support for number value " << t.number_value_ ); + ss << "null"; // NaN or other stuff we cannot represent + } + return ss.str() + "," + newLine; + } } +} // namespace jsonxx::anon::json + +namespace xml { + +std::string escape_attrib( const std::string &input ) { + static std::string map[256]; + static std::once_flag once; + std::call_once(once, [] { + for( int i = 0; i < 256; ++i ) + map[ i ] = "_"; + for( int i = int('a'); i <= int('z'); ++i ) + map[ i ] = std::string() + char(i); + for( int i = int('A'); i <= int('Z'); ++i ) + map[ i ] = std::string() + char(i); + for( int i = int('0'); i <= int('9'); ++i ) + map[ i ] = std::string() + char(i); + }); + + std::string output; + output.reserve( input.size() ); // worst scenario + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + return output; +} + +std::string escape_tag( const std::string &input, unsigned format ) { + static std::string map[256]; + static std::once_flag once; + std::call_once(once, [=] { + for( int i = 0; i < 256; ++i ) + map[ i ] = std::string() + char(i); + map[ byte('<') ] = "<"; + map[ byte('>') ] = ">"; + + switch( format ) + { + default: + break; + + case jsonxx::JXML: + case jsonxx::JXMLex: + case jsonxx::JSONx: + case jsonxx::TaggedXML: + map[ byte('&') ] = "&"; + break; + } + }); - once = map; - } - std::string output; - output.reserve(input.size() * 5); // worst scenario - for (std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it) - output += map[byte(*it)]; - return output; -} - -std::string -open_tag(unsigned format, - char type, - const std::string & name, - const std::string & attr = std::string(), - const std::string & text = std::string()) -{ - std::string tagname; - switch (format) - { - default: - return std::string(); - - case jsonxx::JXML: - if (name.empty()) - tagname = std::string("j son=\"") + type + '\"'; - else - tagname = std::string("j son=\"") + type + ':' + escape_string(name) + '\"'; - break; - - case jsonxx::JXMLex: - if (name.empty()) - tagname = std::string("j son=\"") + type + '\"'; - else - tagname = std::string("j son=\"") + type + ':' + escape_string(name) + "\" " + escape_attrib(name) + "=\"" + - escape_string(text) + "\""; - break; - - case jsonxx::JSONx: - if (!name.empty()) - tagname = std::string(" name=\"") + escape_string(name) + "\""; - switch (type) - { - default: - case '0': - tagname = "json:null" + tagname; - break; - case 'b': - tagname = "json:boolean" + tagname; - break; - case 'a': - tagname = "json:array" + tagname; - break; - case 's': - tagname = "json:string" + tagname; - break; - case 'o': - tagname = "json:object" + tagname; - break; - case 'n': - tagname = "json:number" + tagname; - break; - } - break; - - case jsonxx::TaggedXML: // @TheMadButcher - if (!name.empty()) - tagname = escape_attrib(name); - else - tagname = "JsonItem"; - switch (type) - { + std::string output; + output.reserve( input.size() * 5 ); // worst scenario + for( std::string::const_iterator it = input.begin(), end = input.end(); it != end; ++it ) + output += map[ byte(*it) ]; + return output; +} + +std::string open_tag( unsigned format, char type, const std::string &name, const std::string &attr = std::string(), const std::string &text = std::string() ) { + std::string tagname; + switch( format ) + { default: - case '0': - tagname += " type=\"json:null\""; - break; - case 'b': - tagname += " type=\"json:boolean\""; - break; - case 'a': - tagname += " type=\"json:array\""; - break; - case 's': - tagname += " type=\"json:string\""; - break; - case 'o': - tagname += " type=\"json:object\""; - break; - case 'n': - tagname += " type=\"json:number\""; - break; - } + return std::string(); - if (!name.empty()) - tagname += std::string(" name=\"") + escape_string(name) + "\""; + case jsonxx::JXML: + if( name.empty() ) + tagname = std::string("j son=\"") + type + '\"'; + else + tagname = std::string("j son=\"") + type + ':' + escape_string(name) + '\"'; + break; - break; - } + case jsonxx::JXMLex: + if( name.empty() ) + tagname = std::string("j son=\"") + type + '\"'; + else + tagname = std::string("j son=\"") + type + ':' + escape_string(name) + "\" " + escape_attrib(name) + "=\"" + escape_string(text) + "\""; + break; - return std::string("<") + tagname + attr + ">"; -} + case jsonxx::JSONx: + if( !name.empty() ) + tagname = std::string(" name=\"") + escape_string(name) + "\""; + switch( type ) { + default: + case '0': tagname = "json:null" + tagname; break; + case 'b': tagname = "json:boolean" + tagname; break; + case 'a': tagname = "json:array" + tagname; break; + case 's': tagname = "json:string" + tagname; break; + case 'o': tagname = "json:object" + tagname; break; + case 'n': tagname = "json:number" + tagname; break; + } + break; -std::string -close_tag(unsigned format, char type, const std::string & name) -{ - switch (format) - { - default: - return std::string(); + case jsonxx::TaggedXML: // @TheMadButcher + if( !name.empty() ) + tagname = escape_attrib(name); + else + tagname = "JsonItem"; + switch( type ) { + default: + case '0': tagname += " type=\"json:null\""; break; + case 'b': tagname += " type=\"json:boolean\""; break; + case 'a': tagname += " type=\"json:array\""; break; + case 's': tagname += " type=\"json:string\""; break; + case 'o': tagname += " type=\"json:object\""; break; + case 'n': tagname += " type=\"json:number\""; break; + } + + if( !name.empty() ) + tagname += std::string(" name=\"") + escape_string(name) + "\""; - case jsonxx::JXML: - case jsonxx::JXMLex: - return ""; + break; + } + + return std::string("<") + tagname + attr + ">"; +} - case jsonxx::JSONx: - switch (type) - { +std::string close_tag( unsigned format, char type, const std::string &name ) { + switch( format ) + { default: - case '0': - return ""; - case 'b': - return ""; - case 'a': - return ""; - case 'o': - return ""; - case 's': - return ""; - case 'n': - return ""; - } - break; + return std::string(); + + case jsonxx::JXML: + case jsonxx::JXMLex: + return ""; + + case jsonxx::JSONx: + switch( type ) { + default: + case '0': return ""; + case 'b': return ""; + case 'a': return ""; + case 'o': return ""; + case 's': return ""; + case 'n': return ""; + } + break; - case jsonxx::TaggedXML: // @TheMadButcher - if (!name.empty()) - return ""; - else - return ""; - } + case jsonxx::TaggedXML: // @TheMadButcher + if( !name.empty() ) + return ""; + else + return ""; + } } -std::string -tag(unsigned format, - unsigned depth, - const std::string & name, - const jsonxx::Value & t, - const std::string & attr = std::string()) -{ - std::stringstream ss; - const std::string tab(depth, '\t'); - - switch (t.type_) - { - default: - case jsonxx::Value::NULL_: - return tab + open_tag(format, '0', name, " /") + '\n'; - - case jsonxx::Value::BOOL_: - ss << (t.bool_value_ ? "true" : "false"); - return tab + open_tag(format, 'b', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 'b', name) + '\n'; - - case jsonxx::Value::ARRAY_: - for (Array::container::const_iterator it = t.array_value_->values().begin(), end = t.array_value_->values().end(); - it != end; - ++it) - ss << tag(format, depth + 1, std::string(), **it); - return tab + open_tag(format, 'a', name, attr) + '\n' + ss.str() + tab + close_tag(format, 'a', name) + '\n'; - - case jsonxx::Value::STRING_: - ss << escape_tag(*t.string_value_, format); - return tab + open_tag(format, 's', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 's', name) + '\n'; - - case jsonxx::Value::OBJECT_: - for (Object::container::const_iterator it = t.object_value_->kv_map().begin(), - end = t.object_value_->kv_map().end(); - it != end; - ++it) - ss << tag(format, depth + 1, it->first, *it->second); - return tab + open_tag(format, 'o', name, attr) + '\n' + ss.str() + tab + close_tag(format, 'o', name) + '\n'; - - case jsonxx::Value::NUMBER_: - // max precision - ss << std::setprecision(std::numeric_limits::digits10 + 1); - ss << t.number_value_; - return tab + open_tag(format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string()) + - ss.str() + close_tag(format, 'n', name) + '\n'; - } +std::string tag( unsigned format, unsigned depth, const std::string &name, const jsonxx::Value &t, const std::string &attr = std::string(), int floatPrecision = std::numeric_limits::digits10 + 1) { + std::stringstream ss; + const std::string tab(depth, '\t'); + + switch( t.type_ ) + { + default: + case jsonxx::Value::NULL_: + return tab + open_tag( format, '0', name, " /" ) + '\n'; + + case jsonxx::Value::BOOL_: + ss << ( t.bool_value_ ? "true" : "false" ); + return tab + open_tag( format, 'b', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 'b', name ) + '\n'; + + case jsonxx::Value::ARRAY_: + for(Array::container::const_iterator it = t.array_value_->values().begin(), + end = t.array_value_->values().end(); it != end; ++it ) + ss << tag( format, depth+1, std::string(), **it, std::string(), floatPrecision ); + return tab + open_tag( format, 'a', name, attr ) + '\n' + + ss.str() + + tab + close_tag( format, 'a', name ) + '\n'; + + case jsonxx::Value::STRING_: + ss << escape_tag( *t.string_value_, format ); + return tab + open_tag( format, 's', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 's', name ) + '\n'; + + case jsonxx::Value::OBJECT_: + for(Object::container::const_iterator it=t.object_value_->kv_map().begin(), + end = t.object_value_->kv_map().end(); it != end ; ++it) + ss << tag( format, depth+1, it->first, *it->second, std::string(), floatPrecision ); + return tab + open_tag( format, 'o', name, attr ) + '\n' + + ss.str() + + tab + close_tag( format, 'o', name ) + '\n'; + + case jsonxx::Value::NUMBER_: + // max precision + ss << std::setprecision(floatPrecision); + ss << t.number_value_; + return tab + open_tag( format, 'n', name, std::string(), format == jsonxx::JXMLex ? ss.str() : std::string() ) + + ss.str() + + close_tag( format, 'n', name ) + '\n'; + } } // order here matches jsonxx::Format enum -const char * defheader[] = { "", +const char *defheader[] = { + "", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n", + "" + JSONXX_XML_TAG "\n", - "" JSONXX_XML_TAG "\n" }; + "" + JSONXX_XML_TAG "\n" +}; // order here matches jsonxx::Format enum -const char * defrootattrib[] = { "", +const char *defrootattrib[] = { + "", - " xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\"" - " xmlns:xsi=\"https://www.w3.org/2001/XMLSchema-instance\"" - " xmlns:json=\"https://www.ibm.com/xmlns/prod/2009/jsonx\"", + " xsi:schemaLocation=\"http://www.datapower.com/schemas/json jsonx.xsd\"" + " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" + " xmlns:json=\"http://www.ibm.com/xmlns/prod/2009/jsonx\"", - "", + "", - "", + "", - "" }; + "" +}; -} // namespace xml +} // namespace jsonxx::anon::xml -} // namespace +} // namespace jsonxx::anon -std::string -Object::json() const -{ - using namespace json; +std::string Object::json(PrintMode printMode, int floatPrecision) const { + using namespace json; - jsonxx::Value v; - v.object_value_ = const_cast(this); - v.type_ = jsonxx::Value::OBJECT_; + jsonxx::Value v; + v.object_value_ = const_cast(this); + v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag(jsonxx::JSON, 0, std::string(), v); + std::string result = tag( jsonxx::JSON, 0, std::string(), v, printMode, floatPrecision ); - v.object_value_ = 0; - return remove_last_comma(result); + v.object_value_ = 0; + return remove_last_comma( result ); } -std::string -Object::xml(unsigned format, const std::string & header, const std::string & attrib) const -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); +std::string Object::xml( unsigned format, const std::string &header, const std::string &attrib, int floatPrecision ) const { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); - jsonxx::Value v; - v.object_value_ = const_cast(this); - v.type_ = jsonxx::Value::OBJECT_; + jsonxx::Value v; + v.object_value_ = const_cast(this); + v.type_ = jsonxx::Value::OBJECT_; - std::string result = tag(format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib, floatPrecision ); - v.object_value_ = 0; - return (header.empty() ? std::string(defheader[format]) : header) + result; + v.object_value_ = 0; + return ( header.empty() ? std::string(defheader[format]) : header ) + result; } -std::string -Array::json() const -{ - using namespace json; +std::string Array::json(PrintMode printMode, int floatPrecision) const { + using namespace json; - jsonxx::Value v; - v.array_value_ = const_cast(this); - v.type_ = jsonxx::Value::ARRAY_; + jsonxx::Value v; + v.array_value_ = const_cast(this); + v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag(jsonxx::JSON, 0, std::string(), v); + std::string result = tag( jsonxx::JSON, 0, std::string(), v, printMode, floatPrecision); - v.array_value_ = 0; - return remove_last_comma(result); + v.array_value_ = 0; + return remove_last_comma( result ); } -std::string -Array::xml(unsigned format, const std::string & header, const std::string & attrib) const -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); +std::string Array::xml( unsigned format, const std::string &header, const std::string &attrib, int floatPrecision ) const { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); - jsonxx::Value v; - v.array_value_ = const_cast(this); - v.type_ = jsonxx::Value::ARRAY_; + jsonxx::Value v; + v.array_value_ = const_cast(this); + v.type_ = jsonxx::Value::ARRAY_; - std::string result = tag(format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib); + std::string result = tag( format, 0, std::string(), v, attrib.empty() ? std::string(defrootattrib[format]) : attrib, floatPrecision ); - v.array_value_ = 0; - return (header.empty() ? std::string(defheader[format]) : header) + result; + v.array_value_ = 0; + return ( header.empty() ? std::string(defheader[format]) : header ) + result; } -bool -validate(std::istream & input) -{ +bool validate( std::istream &input ) { - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); - // validate json - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return true; - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return true; - } + // validate json + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return true; + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return true; + } - // bad json input - return false; + // bad json input + return false; } -bool -validate(const std::string & input) -{ - std::istringstream is(input); - return jsonxx::validate(is); +bool validate( const std::string &input ) { + std::istringstream is( input ); + return jsonxx::validate( is ); } -std::string -reformat(std::istream & input) -{ +std::string reformat( std::istream &input ) { - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); - // validate json - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return o.json(); - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return a.json(); - } + // validate json + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return o.json(); + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return a.json(); + } - // bad json input - return std::string(); + // bad json input + return std::string(); } -std::string -reformat(const std::string & input) -{ - std::istringstream is(input); - return jsonxx::reformat(is); -} - -std::string -xml(std::istream & input, unsigned format) -{ - using namespace xml; - JSONXX_ASSERT(format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || - format == jsonxx::TaggedXML); - - // trim non-printable chars - for (char ch(0); !input.eof() && input.peek() <= 32;) - input.get(ch); - - // validate json, then transform - if (input.peek() == '{') - { - jsonxx::Object o; - if (parse_object(input, o)) - return o.xml(format); - } - else if (input.peek() == '[') - { - jsonxx::Array a; - if (parse_array(input, a)) - return a.xml(format); - } +std::string reformat( const std::string &input ) { + std::istringstream is( input ); + return jsonxx::reformat( is ); +} + +std::string xml( std::istream &input, unsigned format ) { + using namespace xml; + JSONXX_ASSERT( format == jsonxx::JSONx || format == jsonxx::JXML || format == jsonxx::JXMLex || format == jsonxx::TaggedXML ); + + // trim non-printable chars + for( char ch(0); !input.eof() && input.peek() <= 32; ) + input.get(ch); - // bad json, return empty xml - return defheader[format]; + // validate json, then transform + if( input.peek() == '{' ) + { + jsonxx::Object o; + if( parse_object( input, o ) ) + return o.xml(format); + } + else + if( input.peek() == '[' ) + { + jsonxx::Array a; + if( parse_array( input, a ) ) + return a.xml(format); + } + + // bad json, return empty xml + return defheader[format]; } -std::string -xml(const std::string & input, unsigned format) -{ - std::istringstream is(input); - return jsonxx::xml(is, format); +std::string xml( const std::string &input, unsigned format ) { + std::istringstream is( input ); + return jsonxx::xml( is, format ); } -Object::Object(const Object & other) -{ +Object::Object(const Object &other) { import(other); } -Object::Object(const std::string & key, const Value & value) -{ - import(key, value); +Object::Object(const std::string &key, const Value &value) { + import(key,value); } -void -Object::import(const Object & other) -{ +void Object::import( const Object &other ) { odd.clear(); - if (this != &other) - { + if (this != &other) { // default - container::const_iterator it = other.value_map_.begin(), end = other.value_map_.end(); - for (/**/; it != end; ++it) - { + container::const_iterator + it = other.value_map_.begin(), + end = other.value_map_.end(); + for (/**/ ; it != end ; ++it) { container::iterator found = value_map_.find(it->first); - if (found != value_map_.end()) - { + if( found != value_map_.end() ) { delete found->second; } - value_map_[it->first] = new Value(*it->second); + value_map_[ it->first ] = new Value( *it->second ); } - } - else - { + } else { // recursion is supported here - import(Object(*this)); + import( Object(*this) ); } } -void -Object::import(const std::string & key, const Value & value) -{ +void Object::import( const std::string &key, const Value &value ) { odd.clear(); container::iterator found = value_map_.find(key); - if (found != value_map_.end()) - { + if( found != value_map_.end() ) { delete found->second; } - value_map_[key] = new Value(value); + value_map_[ key ] = new Value( value ); } -Object & -Object::operator=(const Object & other) -{ +Object &Object::operator=(const Object &other) { odd.clear(); - if (this != &other) - { + if (this != &other) { reset(); import(other); } return *this; } -Object & -Object::operator<<(const Value & value) -{ - if (odd.empty()) - { +Object &Object::operator<<(const Value &value) { + if (odd.empty()) { odd = value.get(); - } - else - { - import(Object(odd, value)); + } else { + import( Object(odd, value) ); odd.clear(); } return *this; } -Object & -Object::operator<<(const Object & value) -{ - import(std::string(odd), value); +Object &Object::operator<<(const Object &value) { + import( std::string(odd),value); odd.clear(); return *this; } -size_t -Object::size() const -{ +size_t Object::size() const { return value_map_.size(); } -bool -Object::empty() const -{ +bool Object::empty() const { return value_map_.size() == 0; } -const std::map & -Object::kv_map() const -{ +const std::map &Object::kv_map() const { return value_map_; } -std::string -Object::write(unsigned format) const -{ +std::string Object::write( unsigned format ) const { return format == JSON ? json() : xml(format); } -void -Object::reset() -{ +void Object::reset() { container::iterator i; - for (i = value_map_.begin(); i != value_map_.end(); ++i) - { + for (i = value_map_.begin(); i != value_map_.end(); ++i) { delete i->second; } value_map_.clear(); } -bool -Object::parse(std::istream & input) -{ - return parse(input, *this); +bool Object::parse(std::istream &input) { + return parse(input,*this); } -bool -Object::parse(const std::string & input) -{ - std::istringstream is(input); - return parse(is, *this); +bool Object::parse(const std::string &input) { + std::istringstream is( input ); + return parse(is,*this); } -Array::Array(const Array & other) -{ +Array::Array(const Array &other) { import(other); } -Array::Array(const Value & value) -{ +Array::Array(const Value &value) { import(value); } -void -Array::append(const Array & other) -{ - if (this != &other) - { - values_.push_back(new Value(other)); - } - else - { - append(Array(*this)); - } -} -void -Array::import(const Array & other) -{ - if (this != &other) - { +void Array::import(const Array &other) { + if (this != &other) { // default - container::const_iterator it = other.values_.begin(), end = other.values_.end(); - for (/**/; it != end; ++it) - { - values_.push_back(new Value(**it)); + container::const_iterator + it = other.values_.begin(), + end = other.values_.end(); + for (/**/ ; it != end; ++it) { + values_.push_back( new Value(**it) ); } - } - else - { + } else { // recursion is supported here - import(Array(*this)); + import( Array(*this) ); } } -void -Array::import(const Value & value) -{ - values_.push_back(new Value(value)); +void Array::import(const Value &value) { + values_.push_back( new Value(value) ); } -size_t -Array::size() const -{ +size_t Array::size() const { return values_.size(); } -bool -Array::empty() const -{ +bool Array::empty() const { return values_.size() == 0; } -void -Array::reset() -{ - for (container::iterator i = values_.begin(); i != values_.end(); ++i) - { +void Array::reset() { + for (container::iterator i = values_.begin(); i != values_.end(); ++i) { delete *i; } values_.clear(); } -bool -Array::parse(std::istream & input) -{ - return parse(input, *this); +bool Array::parse(std::istream &input) { + return parse(input,*this); } -bool -Array::parse(const std::string & input) -{ +bool Array::parse(const std::string &input) { std::istringstream is(input); - return parse(is, *this); + return parse(is,*this); } -Array & -Array::operator<<(const Array & other) -{ +Array &Array::operator<<(const Array &other) { import(other); return *this; } -Array & -Array::operator<<(const Value & value) -{ +Array &Array::operator<<(const Value &value) { import(value); return *this; } -Array & -Array::operator=(const Array & other) -{ - if (this != &other) - { +Array &Array::operator=(const Array &other) { + if( this != &other ) { reset(); import(other); } return *this; } -Array & -Array::operator=(const Value & value) -{ +Array &Array::operator=(const Value &value) { reset(); import(value); return *this; } -Value::Value(const Value & other) - : type_(INVALID_) -{ - import(other); +Value::Value(const Value &other) : type_(INVALID_) { + import( other ); } -bool -Value::empty() const -{ - if (type_ == INVALID_) - return true; - if (type_ == STRING_ && string_value_ == 0) - return true; - if (type_ == ARRAY_ && array_value_ == 0) - return true; - if (type_ == OBJECT_ && object_value_ == 0) - return true; +bool Value::empty() const { + if( type_ == INVALID_ ) return true; + if( type_ == STRING_ && string_value_ == 0 ) return true; + if( type_ == ARRAY_ && array_value_ == 0 ) return true; + if( type_ == OBJECT_ && object_value_ == 0 ) return true; return false; } -bool -Value::parse(std::istream & input) -{ - return parse(input, *this); +bool Value::parse(std::istream &input) { + return parse(input,*this); } -bool -Value::parse(const std::string & input) -{ - std::istringstream is(input); - return parse(is, *this); +bool Value::parse(const std::string &input) { + std::istringstream is( input ); + return parse(is,*this); } -} // namespace jsonxx +} // namespace jsonxx