Mercurial > code
changeset 479:c7d83d2b462b
Merge from change-json-unification
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 10 Nov 2015 14:11:37 +0100 |
parents | 1ff22c1cb32e (current diff) c7825b8c6145 (diff) |
children | 453f22449b33 |
files | |
diffstat | 3 files changed, 757 insertions(+), 635 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Json/Json.cpp Tue Nov 10 11:08:38 2015 +0100 +++ b/C++/modules/Json/Json.cpp Tue Nov 10 14:11:37 2015 +0100 @@ -45,14 +45,14 @@ return Value{json_boolean_value(v)}; } if (json_is_object(v)) { - Object object; + Value object{Type::Object}; readObject(object, v); return object; } if (json_is_array(v)) { - Array array; + Value array{Type::Array}; readArray(array, v); @@ -68,7 +68,7 @@ json_t *value; json_object_foreach(object, key, value) { - static_cast<Object &>(parent).insert(key, readValue(value)); + parent.insert(key, readValue(value)); } } @@ -78,7 +78,7 @@ json_t *value; json_array_foreach(array, index, value) { - static_cast<Array &>(parent).append(readValue(value)); + parent.append(readValue(value)); } } @@ -95,10 +95,10 @@ Value value; if (json_is_object(json)) { - value = Object{}; + value = Value{Type::Object}; readObject(value, json); } else { - value = Array{}; + value = Value{Type::Array}; readArray(value, json); } @@ -109,6 +109,106 @@ } // !namespace +void Value::copy(const Value &other) +{ + switch (other.m_type) { + case Type::Array: + new (&m_array) std::deque<Value>(other.m_array); + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Int: + m_integer = other.m_integer; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(other.m_object); + break; + case Type::Real: + m_number = other.m_number; + break; + case Type::String: + new (&m_string) std::string(other.m_string); + break; + default: + break; + } + + m_type = other.m_type; +} + +void Value::move(Value &&other) +{ + switch (other.m_type) { + case Type::Array: + new (&m_array) std::deque<Value>(std::move(other.m_array)); + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Int: + m_integer = other.m_integer; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(std::move(other.m_object)); + break; + case Type::Real: + m_number = other.m_number; + break; + case Type::String: + new (&m_string) std::string(std::move(other.m_string)); + break; + default: + break; + } + + m_type = other.m_type; +} + +Value::Value(Type type) + : m_type{type} +{ + switch (m_type) { + case Type::Array: + new (&m_array) std::deque<Value>(); + break; + case Type::Boolean: + m_boolean = false; + break; + case Type::Int: + m_integer = 0; + break; + case Type::Object: + new (&m_object) std::map<std::string, Value>(); + break; + case Type::Real: + m_number = 0; + break; + case Type::String: + new (&m_string) std::string(); + break; + default: + break; + } +} + +Value::~Value() +{ + switch (m_type) { + case Type::Array: + m_array.~deque<Value>(); + break; + case Type::Object: + m_object.~map<std::string, Value>(); + break; + case Type::String: + m_string.~basic_string(); + break; + default: + break; + } +} + bool Value::toBool() const noexcept { if (m_type != Type::Boolean) { @@ -145,32 +245,14 @@ return m_string; } -Object Value::toObject() const noexcept +Value::Value(const Buffer &buffer) { - if (m_type != Type::Object) { - return Object{}; - } - - return Object(*this); + *this = convert(json_loads, buffer.text.c_str(), 0); } -Array Value::toArray() const noexcept +Value::Value(const File &file) { - if (m_type != Type::Array) { - return Array{}; - } - - return Array(*this); -} - -Document::Document(Buffer buffer) -{ - m_value = convert(json_loads, buffer.text.c_str(), 0); -} - -Document::Document(File file) -{ - m_value = convert(json_load_file, file.path.c_str(), 0); + *this = convert(json_load_file, file.path.c_str(), 0); } std::string escape(const std::string &value)
--- a/C++/modules/Json/Json.h Tue Nov 10 11:08:38 2015 +0100 +++ b/C++/modules/Json/Json.h Tue Nov 10 14:11:37 2015 +0100 @@ -28,6 +28,7 @@ * you get real copies, thus when you read big documents it can has a performance cost. */ +#include <cassert> #include <exception> #include <initializer_list> #include <map> @@ -146,42 +147,232 @@ } }; -class Array; -class Object; +/** + * @class Buffer + * @brief Open JSON document from text. + */ +class Buffer { +public: + std::string text; //!< The JSON text +}; + +/** + * @class File + * @brief Open JSON document from a file. + */ +class File { +public: + std::string path; //!< The path to the file +}; /** * @class Value * @brief Generic JSON value wrapper. */ class Value { -protected: - /** - * Type of value. - */ - Type m_type; +private: + Type m_type{Type::Null}; - /** - * Union of values. - */ union { double m_number; bool m_boolean; int m_integer; std::string m_string; std::deque<Value> m_array; - std::map<std::string, Value> m_map; + std::map<std::string, Value> m_object; }; + void copy(const Value &); + void move(Value &&); + + /** + * @class BaseIterator + * @brief This is the base class for iterator and const_iterator + * + * This iterator works for both arrays and objects. Because of that purpose, it is only available + * as forward iterator. + * + * When iterator comes from an object, you can use key() otherwise you can use index(). + */ + template <typename ValueType, typename ArrayIteratorType, typename ObjectIteratorType> + class BaseIterator : public std::iterator<std::forward_iterator_tag, ValueType> { + private: + friend class Value; + + ValueType &m_value; + ArrayIteratorType m_ita; + ObjectIteratorType m_itm; + + inline void increment() + { + if (m_value.isObject()) { + m_itm++; + } else { + m_ita++; + } + } + + BaseIterator(ValueType &value, ObjectIteratorType it) + : m_value(value) + , m_itm(it) + { + } + + BaseIterator(ValueType &value, ArrayIteratorType it) + : m_value(value) + , m_ita(it) + { + } + + public: + /** + * Get the iterator key (for objects). + * + * @pre iterator must be dereferenceable + * @pre iterator must come from object + * @return the key + */ + inline const std::string &key() const noexcept + { + assert(m_value.isObject()); + assert(m_itm != m_value.m_object.end()); + + return m_itm->first; + } + + /** + * Get the iterator position (for arrays). + * + * @pre iterator must be dereferenceable + * @pre iterator must come from arrays + * @return the index + */ + inline unsigned index() const noexcept + { + assert(m_value.isArray()); + assert(m_ita != m_value.m_array.end()); + + return std::distance(m_value.m_array.begin(), m_ita); + } + + /** + * Dereference the iterator. + * + * @pre iterator be dereferenceable + * @return the value + */ + inline ValueType &operator*() noexcept + { + assert((m_value.isArray() && m_ita != m_value.m_array.end()) || + (m_value.isObject() && m_itm != m_value.m_object.end())); + + return (m_value.m_type == Type::Object) ? m_itm->second : *m_ita; + } + + /** + * Dereference the iterator as a pointer. + * + * @pre iterator must be dereferenceable + * @return the value + */ + inline ValueType *operator->() noexcept + { + assert((m_value.isArray() && m_ita != m_value.m_array.end()) || + (m_value.isObject() && m_itm != m_value.m_object.end())); + + return (m_value.m_type == Type::Object) ? &m_itm->second : &(*m_ita); + } + + /** + * Increment the iterator. (Prefix version). + * + * @pre iterator must be dereferenceable + * @return *this; + */ + inline BaseIterator &operator++() noexcept + { + assert((m_value.isArray() && m_ita != m_value.m_array.end()) || + (m_value.isObject() && m_itm != m_value.m_object.end())); + + increment(); + + return *this; + } + + /** + * Increment the iterator. (Postfix version). + * + * @pre iterator must be dereferenceable + * @return *this; + */ + inline BaseIterator &operator++(int) noexcept + { + assert((m_value.isArray() && m_ita != m_value.m_array.end()) || + (m_value.isObject() && m_itm != m_value.m_object.end())); + + increment(); + + return *this; + } + + /** + * Compare two iterators. + * + * @param it1 the first iterator + * @param it2 the second iterator + * @return true if they are same + */ + bool operator==(const BaseIterator &it) const noexcept + { + if (m_value.isObject() && it.m_value.isObject()) + return m_itm == it.m_itm; + if (m_value.isArray() && it.m_value.isArray()) + return m_ita == it.m_ita; + + return false; + } + + /** + * Test if the iterator is different. + * + * @param it the iterator + * @return true if they are different + */ + inline bool operator!=(const BaseIterator &it) const noexcept + { + return !(*this == it); + } + }; + + /** + * Forward iterator. + */ + using iterator = BaseIterator<Value, typename std::deque<Value>::iterator, typename std::map<std::string, Value>::iterator>; + + /** + * Const forward iterator. + */ + using const_iterator = BaseIterator<const Value, typename std::deque<Value>::const_iterator, typename std::map<std::string, Value>::const_iterator>; + public: /** * Construct a null value. */ - inline Value() noexcept - : m_type{Type::Null} + inline Value() { } /** + * Create a value with a specified type, this is usually only needed when you want to create an object or + * an array. + * + * For any other types, initialize with sane default value. + * + * @param type the type + */ + Value(Type type); + + /** * Construct a null value. */ inline Value(std::nullptr_t) noexcept @@ -219,7 +410,7 @@ inline Value(const char *value) : m_type{Type::String} { - new (&m_string) std::string{value}; + new (&m_string) std::string{value ? value : ""}; } /** @@ -245,39 +436,57 @@ } /** + * Create an object from a map. + * + * @param values the values + * @see fromObject + */ + inline Value(std::map<std::string, Value> values) + : Value{Type::Object} + { + for (const auto &pair : values) { + insert(pair.first, pair.second); + } + } + + /** + * Create an array from a deque. + * + * @param values the values + * @see fromArray + */ + inline Value(std::deque<Value> values) + : Value{Type::Array} + { + for (Value value : values) { + append(std::move(value)); + } + } + + /** + * Construct a value from a buffer. + * + * @param buffer the text + * @throw Error on errors + */ + Value(const Buffer &buffer); + + /** + * Construct a value from a file. + * + * @param file the file + * @throw Error on errors + */ + Value(const File &file); + + /** * Move constructor. * * @param other the value to move from */ inline Value(Value &&other) { - m_type = other.m_type; - - switch (m_type) { - case Type::String: - new (&m_string) std::string(); - m_string = std::move(other.m_string); - break; - case Type::Real: - m_number = other.m_number; - break; - case Type::Int: - m_integer = other.m_integer; - break; - case Type::Boolean: - m_boolean = other.m_boolean; - break; - case Type::Object: - new (&m_map) std::map<std::string, Value>(); - m_map = std::move(other.m_map); - break; - case Type::Array: - new (&m_array) std::deque<Value>(); - m_array = std::move(other.m_array); - break; - default: - break; - } + move(std::move(other)); } /** @@ -287,33 +496,7 @@ */ inline Value(const Value &other) { - m_type = other.m_type; - - switch (m_type) { - case Type::String: - new (&m_string) std::string(); - m_string = other.m_string; - break; - case Type::Real: - m_number = other.m_number; - break; - case Type::Int: - m_integer = other.m_integer; - break; - case Type::Boolean: - m_boolean = other.m_boolean; - break; - case Type::Object: - new (&m_map) std::map<std::string, Value>(); - m_map = other.m_map; - break; - case Type::Array: - new (&m_array) std::deque<Value>(); - m_array = other.m_array; - break; - default: - break; - } + copy(other); } /** @@ -324,33 +507,7 @@ */ inline Value &operator=(const Value &other) { - m_type = other.m_type; - - switch (m_type) { - case Type::String: - new (&m_string) std::string(); - m_string = other.m_string; - break; - case Type::Real: - m_number = other.m_number; - break; - case Type::Int: - m_integer = other.m_integer; - break; - case Type::Boolean: - m_boolean = other.m_boolean; - break; - case Type::Object: - new (&m_map) std::map<std::string, Value>(); - m_map = other.m_map; - break; - case Type::Array: - new (&m_array) std::deque<Value>(); - m_array = other.m_array; - break; - default: - break; - } + copy(other); return *this; } @@ -362,33 +519,7 @@ */ inline Value &operator=(Value &&other) { - m_type = other.m_type; - - switch (m_type) { - case Type::String: - new (&m_string) std::string(); - m_string = std::move(other.m_string); - break; - case Type::Real: - m_number = other.m_number; - break; - case Type::Int: - m_integer = other.m_integer; - break; - case Type::Boolean: - m_boolean = other.m_boolean; - break; - case Type::Object: - new (&m_map) std::map<std::string, Value>(); - m_map = std::move(other.m_map); - break; - case Type::Array: - new (&m_array) std::deque<Value>(); - m_array = std::move(other.m_array); - break; - default: - break; - } + move(std::move(other)); return *this; } @@ -396,21 +527,84 @@ /** * Destructor. */ - inline ~Value() + ~Value(); + + /** + * Get an iterator to the beginning. + * + * @pre must be an array or object + * @return the iterator + */ + inline iterator begin() noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(*this, m_object.begin()) : iterator(*this, m_array.begin()); + } + + /** + * Overloaded function. + * + * @pre must be an array or object + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.begin()) : const_iterator(*this, m_array.begin()); + } + + /** + * Overloaded function. + * + * @pre must be an array or object + * @return the iterator + */ + inline const_iterator cbegin() const noexcept { - switch (m_type) { - case Type::String: - m_string.~basic_string(); - break; - case Type::Object: - m_map.~map<std::string, Value>(); - break; - case Type::Array: - m_array.~deque<Value>(); - break; - default: - break; - } + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.cbegin()) : const_iterator(*this, m_array.cbegin()); + } + + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ + inline iterator end() noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? iterator(*this, m_object.end()) : iterator(*this, m_array.end()); + } + + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ + inline const_iterator end() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.end()) : const_iterator(*this, m_array.end()); + } + + /** + * Get an iterator to the end. + * + * @pre must be an array or object + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + assert(isArray() || isObject()); + + return m_type == Type::Object ? const_iterator(*this, m_object.cend()) : const_iterator(*this, m_array.cend()); } /** @@ -452,22 +646,6 @@ std::string toString() const noexcept; /** - * Convert the value to object. - * - * @return an object or empty if not an object - * @note the returned object is a copy, modifying it won't modify this value - */ - Object toObject() const noexcept; - - /** - * Convert the value to array. - * - * @return an array or empty if not an array - * @note the returned array is a copy, modifying it won't modify this value - */ - Array toArray() const noexcept; - - /** * Check if the value is boolean type. * * @return true if boolean @@ -548,113 +726,44 @@ { return m_type == Type::String; } -}; - -/** - * @class Array - * @brief Array definition. - */ -class Array : public Value { -public: - /** - * Construct an empty array. - */ - inline Array() - { - m_type = Type::Array; - new (&m_array) std::deque<Value>(); - } /** - * Copy constructor from value for safe casts. + * Get the array or object size. * - * @param v the value to copy from - */ - inline Array(const Value &v) - : Value{v} - { - } - - /** - * Move constructor from value for safe casts. - * - * @param v the value to move from + * @pre must be an array or object + * @return the size */ - inline Array(Value &&v) - : Value{std::move(v)} + inline unsigned size() const noexcept { - } + assert(isArray() || isObject()); - /** - * Construct an array from a std::initializer_list. - * - * @param values the list of values - */ - inline Array(std::initializer_list<Value> values) - { - m_type = Type::Array; - new (&m_array) std::deque<Value>(values.begin(), values.end()); + if (m_type == Type::Object) { + return m_object.size(); + } + + return m_array.size(); } /** - * Get non-const iterator to the beginning. + * Remove all the values. * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_array.begin(); - } - - /** - * Get a const iterator to the beginning. - * - * @return the iterator + * @pre must be an array or an object */ - inline auto begin() const noexcept + inline void clear() noexcept { - return m_array.begin(); - } + assert(isArray() || isObject()); - /** - * Get a const iterator to the beginning. - * - * @return the iterator - */ - inline auto cbegin() const noexcept - { - return m_array.cbegin(); + if (m_type == Type::Array) { + m_array.clear(); + } else { + m_object.clear(); + } } - /** - * Get a non-const iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_array.end(); - } - - /** - * Get a const iterator to the end. - * - * @return the iterator + /* + * Array functions + * ---------------------------------------------------------- */ - inline auto end() const noexcept - { - return m_array.end(); - } - - /** - * Get a const iterator to the end. - * - * @return the iterator - */ - inline auto cend() const noexcept - { - return m_array.cend(); - } /** * Get the value at the specified position or the defaultValue if position is out of bounds. @@ -663,10 +772,10 @@ * @param defaultValue the value replacement * @return the value or defaultValue */ - template <typename V> - inline Value valueOr(unsigned position, V &&defaultValue) const + template <typename DefaultValue> + inline Value valueOr(unsigned position, DefaultValue &&defaultValue) const { - if (position >= m_array.size()) { + if (m_type != Type::Array || position >= m_array.size()) { return defaultValue; } @@ -676,217 +785,168 @@ /** * Get a value at the specified index. * + * @pre must be an array * @param position the position * @return the value * @throw std::out_of_range if out of bounds */ inline const Value &at(unsigned position) const { + assert(isArray()); + return m_array.at(position); } /** - * Get a value at the specified index. + * Overloaded function. * + * @pre must be an array * @param position the position * @return the value * @throw std::out_of_range if out of bounds */ inline Value &at(unsigned position) { + assert(isArray()); + return m_array.at(position); } /** * Get a value at the specified index. * + * @pre must be an array + * @pre position must be valid * @param position the position * @return the value */ inline const Value &operator[](unsigned position) const { + assert(isArray()); + assert(position < m_array.size()); + return m_array[position]; } /** * Overloaded function. * + * @pre must be an array + * @pre position must be valid * @param position the position * @return the value */ inline Value &operator[](unsigned position) { + assert(isArray()); + assert(position < m_array.size()); + return m_array[position]; } /** * Push a value to the beginning of the array. * + * @pre must be an array * @param value the value to push */ - template <typename T> - inline void push(T &&value) + inline void push(const Value &value) { - m_array.push_front(std::forward<T>(value)); + assert(isArray()); + + m_array.push_front(value); + } + + /** + * Overloaded function. + * + * @pre must be an array + * @param value the value to push + */ + inline void push(Value &&value) + { + assert(isArray()); + + m_array.push_front(std::move(value)); } /** * Insert a value at the specified position. * + * @pre must be an array + * @pre position must be valid * @param position the position * @param value the value to push */ - template <typename T> - inline void insert(unsigned position, T &&value) + inline void insert(unsigned position, const Value &value) { - m_array.insert(m_array.begin() + position, std::forward<T>(value)); + assert(isArray()); + assert(position <= m_array.size()); + + m_array.insert(m_array.begin() + position, value); } /** - * Add a new value. + * Overloaded function. * - * @param value the value to append + * @pre must be an array + * @pre position must be valid + * @param position the position + * @param value the value to push */ - template <typename T> - inline void append(T &&value) + inline void insert(unsigned position, Value &&value) { - m_array.push_back(std::forward<T>(value)); + assert(isArray()); + assert(position <= m_array.size()); + + m_array.insert(m_array.begin() + position, std::move(value)); } /** - * Get the array size. + * Add a new value to the end. * - * @return the size + * @pre must be an array + * @param value the value to append */ - inline unsigned size() const noexcept + inline void append(const Value &value) { - return m_array.size(); + assert(isArray()); + + m_array.push_back(value); } /** - * Remove all the values. + * Overloaded function. + * + * @pre must be an array + * @param value the value to append */ - inline void clear() + inline void append(Value &&value) { - m_array.clear(); + assert(isArray()); + + m_array.push_back(std::move(value)); } /** * Remove a value at the specified position. * + * @pre must be an array + * @pre position must be valid * @param position the position */ inline void erase(unsigned position) { - m_array.erase(m_array.begin() + position); - } -}; - -/** - * @class Object - * @brief Object definition. - */ -class Object : public Value { -public: - /** - * Construct an empty object. - */ - Object() - { - m_type = Type::Object; - new (&m_map) std::map<std::string, Value>(); - } + assert(isArray()); + assert(position < m_array.size()); - /** - * Copy constructor from value for safe casts. - * - * @param v the value to copy from - */ - Object(const Value &v) - : Value{v} - { - } - - /** - * Move constructor from value for safe casts. - * - * @param v the value to move from - */ - Object(Value &&v) - : Value{std::move(v)} - { - } - - /** - * Create an object from the initializer_list of key-value pairs. - * - * @param values the values - */ - Object(std::initializer_list<std::pair<std::string, Value>> values) - { - m_type = Type::Object; - new (&m_map) std::map<std::string, Value>(values.begin(), values.end()); + m_array.erase(m_array.begin() + position); } - /** - * Get non-const iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() noexcept - { - return m_map.begin(); - } - - /** - * Get a const iterator to the beginning. - * - * @return the iterator - */ - inline auto begin() const noexcept - { - return m_map.begin(); - } - - /** - * Get a const iterator to the beginning. - * - * @return the iterator + /* + * Object functions + * ---------------------------------------------------------- */ - inline auto cbegin() const noexcept - { - return m_map.cbegin(); - } - - /** - * Get a non-const iterator to the end. - * - * @return the iterator - */ - inline auto end() noexcept - { - return m_map.end(); - } - - /** - * Get a const iterator to the end. - * - * @return the iterator - */ - inline auto end() const noexcept - { - return m_map.end(); - } - - /** - * Get a const iterator to the end. - * - * @return the iterator - */ - inline auto cend() const noexcept - { - return m_map.cend(); - } /** * Get the value at the specified key or the defaultValue if key is absent. @@ -895,12 +955,16 @@ * @param defaultValue the value replacement * @return the value or defaultValue */ - template <typename V> - inline Value valueOr(const std::string &name, V &&defaultValue) const + template <typename DefaultValue> + Value valueOr(const std::string &name, DefaultValue &&defaultValue) const { - auto it = m_map.find(name); + if (m_type != Type::Object) { + return defaultValue; + } - if (it == m_map.end()) { + auto it = m_object.find(name); + + if (it == m_object.end()) { return defaultValue; } @@ -910,180 +974,142 @@ /** * Get a value from the object. * + * @pre must be an object * @param name the value key * @return the value * @throw std::out_of_range if not found */ inline const Value &at(const std::string &name) const { - return m_map.at(name); + assert(isObject()); + + return m_object.at(name); } /** * Overloaded function. * + * @pre must be an object * @param name the value key * @return the value * @throw std::out_of_range if not found */ inline Value &at(const std::string &name) { - return m_map.at(name); + assert(isObject()); + + return m_object.at(name); + } + + /** + * Get a value from the object. + * + * @pre must be an object + * @param name the value key + * @return the value + */ + inline Value &operator[](const std::string &name) + { + assert(isObject()); + + return m_object[name]; } /** * Get a value from the object. * + * @pre must be an object * @param name the value key * @return the value */ - inline Value &operator[](const std::string &name) + inline const Value &operator[](const std::string &name) const + { + assert(isObject()); + + return m_object.at(name); + } + + /** + * Find a value by key. + * + * @pre must be an object + * @param key the property key + * @return the iterator or past the end if not found + */ + inline iterator find(const std::string &key) { - return m_map.at(name); + assert(isObject()); + + return iterator(*this, m_object.find(key)); + } + + /** + * Overloaded function. + * + * @pre must be an object + * @param key the property key + * @return the iterator or past the end if not found + */ + inline const_iterator find(const std::string &key) const + { + assert(isObject()); + + return const_iterator(*this, m_object.find(key)); } /** * Insert a new value. * + * @pre must be an object * @param name the key * @param value the value */ - inline void insert(std::string name, Value value) + inline void insert(std::string name, const Value &value) { - m_map.insert({std::move(name), std::move(value)}); + assert(isObject()); + + m_object.insert({std::move(name), value}); } /** - * Remove all the values from the object. + * Overloaded function. + * + * @pre must be an object + * @param name the key + * @param value the value */ - inline void clear() + inline void insert(std::string name, Value &&value) { - m_map.clear(); - } + assert(isObject()); - /** - * Get the number of entries in the object. - * - * @return the size - */ - inline unsigned size() const noexcept - { - return m_map.size(); + m_object.insert({std::move(name), std::move(value)}); } /** * Check if a value exists. * + * @pre must be an object * @param key the key value * @return true if exists */ inline bool contains(const std::string &key) const noexcept { - return m_map.find(key) != m_map.end(); + assert(isObject()); + + return m_object.find(key) != m_object.end(); } /** * Remove a value of the specified key. * + * @pre must be an object * @param key the value key */ inline void erase(const std::string &key) { - m_map.erase(key); - } -}; - -/** - * @class Buffer - * @brief Open JSON document from text. - */ -class Buffer { -public: - std::string text; //!< The JSON text -}; - -/** - * @class File - * @brief Open JSON document from a file. - */ -class File { -public: - std::string path; //!< The path to the file -}; - -/** - * @class Document - * @brief Construct a JSON document from a source - * @see Buffer - * @see File - */ -class Document { -private: - Value m_value; - -public: - /** - * Construct a document from a buffer. - * - * @param buffer the text - * @throw Error on errors - */ - Document(Buffer buffer); + assert(isObject()); - /** - * Construct a document from a file. - * - * @param file the file - * @throw Error on errors - */ - Document(File file); - - /** - * Check if the opened document is an object. - * - * @return true if object - */ - inline bool isObject() const noexcept - { - return m_value.typeOf() == Type::Object; - } - - /** - * Check if the opened document is an array. - * - * @return true if array - */ - inline bool isArray() const noexcept - { - return m_value.typeOf() == Type::Array; - } - - /** - * Get the object. - * - * @return the object or empty object if not an object - */ - inline Object toObject() const noexcept - { - if (m_value.typeOf() != Type::Object) { - return Object{}; - } - - return Object(m_value); - } - - /** - * Get the array. - * - * @return the array or empty object if not an array - */ - inline Array toArray() const noexcept - { - if (m_value.typeOf() != Type::Array) { - return Array{}; - } - - return Array(m_value); + m_object.erase(key); } }; @@ -1095,6 +1121,28 @@ */ std::string escape(const std::string &input); +/** + * Convenient function for creating array from initializer list. + * + * @param values the values + * @return the array + */ +inline Value array(std::initializer_list<Value> values) +{ + return Value(std::deque<Value>(values.begin(), values.end())); +} + +/** + * Convenient function for creating object from initializer list. + * + * @param values the values + * @return the object + */ +inline Value object(std::initializer_list<std::pair<std::string, Value>> values) +{ + return Value(std::map<std::string, Value>(values.begin(), values.end())); +} + } // !json #endif // !_JSON_H_
--- a/C++/tests/Json/main.cpp Tue Nov 10 11:08:38 2015 +0100 +++ b/C++/tests/Json/main.cpp Tue Nov 10 14:11:37 2015 +0100 @@ -23,18 +23,19 @@ #include "Json.h" -/* -------------------------------------------------------- +/* * Miscellaneous - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Misc, copy) { - json::Object object; + json::Value object{json::Type::Object}; object.insert("integer", 123); object.insert("true", true); - json::Object object2{object}; + json::Value object2{object}; ASSERT_TRUE(object2.isObject()); ASSERT_EQ(123, object2["integer"].toInt()); @@ -43,10 +44,10 @@ TEST(Misc, copyAssign) { - json::Object object; + json::Value object{json::Type::Object}; { - json::Object tmp; + json::Value tmp{json::Type::Object}; tmp.insert("integer", 123); tmp.insert("true", true); @@ -61,8 +62,8 @@ TEST(Misc, move) { - json::Object object(123); - json::Object object2(std::move(object)); + json::Value object(123); + json::Value object2(std::move(object)); ASSERT_TRUE(object2.isInt()); ASSERT_EQ(123, object2.toInt()); @@ -70,8 +71,8 @@ TEST(Misc, moveAssign) { - json::Object object(123); - json::Object object2; + json::Value object(123); + json::Value object2; object2 = std::move(object); @@ -87,9 +88,10 @@ ASSERT_EQ(expected, json::escape(input)); } -/* -------------------------------------------------------- +/* * json::Value constructors - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Constructors, null) { @@ -154,14 +156,15 @@ } } -/* -------------------------------------------------------- +/* * Object - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Object, set) { try { - json::Object object; + json::Value object{json::Type::Object}; object.insert("integer", 123); object.insert("string", "hello"); @@ -178,7 +181,7 @@ TEST(Object, clear) { try { - json::Object object; + json::Value object{json::Type::Object}; object.insert("integer", 123); object.insert("string", "hello"); @@ -198,7 +201,7 @@ TEST(Object, erase) { try { - json::Object object; + json::Value object{json::Type::Object}; object.insert("integer", 123); object.insert("string", "hello"); @@ -218,10 +221,10 @@ TEST(Object, valueOr) { try { - json::Object object{ + json::Value object = json::object({ { "x", 10 }, { "y", 20 } - }; + }); ASSERT_EQ(10, object.valueOr("x", -9999).toInt()); ASSERT_EQ(20, object.valueOr("y", -9999).toInt()); @@ -234,10 +237,10 @@ TEST(ObjectInitializer, simple) { try { - json::Object object{ + json::Value object = json::object({ { "username", "jean" }, { "age", 99 } - }; + }); ASSERT_EQ(2, static_cast<int>(object.size())); ASSERT_EQ("jean", object["username"].toString()); @@ -250,15 +253,15 @@ TEST(ObjectInitializer, deep) { try { - json::Object object{ + json::Value object = json::object({ { "username", "jean" }, { "age", 99 }, - { "network", json::Object{ + { "network", json::object({ { "port", 9999 }, { "host", "localhost" } - } + }) } - }; + }); // First ASSERT_EQ(3, static_cast<int>(object.size())); @@ -266,7 +269,7 @@ ASSERT_EQ(99, object["age"].toInt()); // Second - json::Object network = object["network"].toObject(); + const json::Value &network = object["network"]; ASSERT_TRUE(network.isObject()); ASSERT_EQ(2, static_cast<int>(network.size())); ASSERT_EQ(9999, network["port"].toInt()); @@ -276,14 +279,15 @@ } } -/* -------------------------------------------------------- +/* * Array - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(Array, push) { try { - json::Array array; + json::Value array{json::Type::Array}; ASSERT_TRUE(array.isArray()); @@ -303,7 +307,7 @@ TEST(Array, append) { try { - json::Array array; + json::Value array{json::Type::Array}; ASSERT_TRUE(array.isArray()); @@ -323,7 +327,7 @@ TEST(Array, insert) { try { - json::Array array; + json::Value array{json::Type::Array}; ASSERT_TRUE(array.isArray()); @@ -343,7 +347,7 @@ TEST(Array, clear) { try { - json::Array array; + json::Value array{json::Type::Array}; array.append(1); array.append("hello"); @@ -360,7 +364,7 @@ TEST(Array, erase) { try { - json::Array array; + json::Value array{json::Type::Array}; array.append(1); array.append("hello"); @@ -379,7 +383,7 @@ TEST(Array, valueOr) { try { - json::Array array{-10, -20}; + json::Value array = json::array({-10, -20}); ASSERT_EQ(-10, array.valueOr(0, -9999).toInt()); ASSERT_EQ(-20, array.valueOr(1, -9999).toInt()); @@ -392,7 +396,7 @@ TEST(ArrayInitializer, simple) { try { - json::Array array{123, true, "hello"}; + json::Value array = json::array({123, true, "hello"}); ASSERT_EQ(3, static_cast<int>(array.size())); ASSERT_EQ(123, array[0].toInt()); @@ -406,16 +410,16 @@ TEST(ArrayInitializer, deep) { try { - json::Array array{ + json::Value array = json::array({ 123, true, "hello", - json::Array{ + json::array({ 321, false, "olleh" - } - }; + }) + }); // First ASSERT_EQ(4, static_cast<int>(array.size())); @@ -424,7 +428,7 @@ ASSERT_EQ("hello", array[2].toString()); // Second - json::Array array2 = array[3].toArray(); + const json::Value &array2 = array[3]; ASSERT_TRUE(array.isArray()); ASSERT_EQ(3, static_cast<int>(array2.size())); ASSERT_EQ(321, array2[0].toInt()); @@ -435,14 +439,15 @@ } } -/* -------------------------------------------------------- +/* * I/O - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ TEST(FileRead, simple) { try { - json::Document doc(json::File{"Json/simple.json"}); + json::Value doc(json::File{"Json/simple.json"}); ASSERT_TRUE(doc.isObject()); ASSERT_FALSE(doc.isArray()); @@ -454,7 +459,7 @@ TEST(FileRead, fail) { try { - json::Document(json::File{"Json/notexist.json"}); + json::Value(json::File{"Json/notexist.json"}); FAIL() << "Exception expected"; } catch (const json::Error &) { @@ -464,7 +469,7 @@ TEST(StringRead, simple) { try { - json::Document doc(json::Buffer{"{ \"license\": \"ISC\" }"}); + json::Value doc(json::Buffer{"{ \"license\": \"ISC\" }"}); ASSERT_TRUE(doc.isObject()); ASSERT_FALSE(doc.isArray()); @@ -476,27 +481,28 @@ TEST(StringRead, fail) { try { - json::Document(json::Buffer{"{ \"license\": ISC }"}); + json::Value(json::Buffer{"{ \"license\": ISC }"}); FAIL() << "Exception expected"; } catch (const json::Error &ex) { } } -/* -------------------------------------------------------- +/* * Object read - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ObjectRead : public testing::Test { protected: - json::Object m_object; - json::Object m_objectAll; + json::Value m_object; + json::Value m_objectAll; public: ObjectRead() + : m_object(json::File{"Json/object.json"}) + , m_objectAll(json::File{"Json/object-all.json"}) { - m_object = json::Document(json::File{"Json/object.json"}).toObject(); - m_objectAll = json::Document(json::File{"Json/object-all.json"}).toObject(); } }; @@ -546,20 +552,21 @@ } } -/* -------------------------------------------------------- +/* * Array read - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ArrayRead : public testing::Test { protected: - json::Array m_array; - json::Array m_arrayAll; + json::Value m_array; + json::Value m_arrayAll; public: ArrayRead() + : m_array(json::File{"Json/array.json"}) + , m_arrayAll(json::File{"Json/array-all.json"}) { - m_array = json::Document(json::File{"Json/array.json"}).toArray(); - m_arrayAll = json::Document(json::File{"Json/array-all.json"}).toArray(); } }; @@ -603,13 +610,14 @@ } } -/* -------------------------------------------------------- +/* * Object iterators - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ObjectIteratorsTest : public testing::Test { protected: - json::Object m_object; + json::Value m_object{json::Type::Object}; public: ObjectIteratorsTest() @@ -631,8 +639,9 @@ auto end = m_object.end(); while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; } ASSERT_EQ(expected, result); @@ -650,8 +659,9 @@ auto end = m_object.cend(); while (it != end) { - values.insert({it->first, it->second}); - result.insert((*it++).first); + values.insert({it.key(), *it}); + result.insert(it.key()); + it++; } ASSERT_EQ(expected, result); @@ -666,23 +676,39 @@ // Assign (non const) { auto it = m_object.begin(); - auto key = it->first; + auto key = it.key(); - it->second = json::Value("CHANGED"); + *it = json::Value("CHANGED"); ASSERT_EQ("CHANGED", m_object[key].toString()); - ASSERT_EQ("CHANGED", it->second.toString()); + ASSERT_EQ("CHANGED", it->toString()); ASSERT_EQ(3, static_cast<int>(m_object.size())); } } -/* -------------------------------------------------------- +TEST_F(ObjectIteratorsTest, find) +{ + auto it = m_object.find("integer"); + + ASSERT_TRUE(it != m_object.end()); + ASSERT_EQ(1, it->toInt()); +} + +TEST_F(ObjectIteratorsTest, find2) +{ + auto it = m_object.find("not exists"); + + ASSERT_TRUE(it == m_object.end()); +} + +/* * Array iterators - * -------------------------------------------------------- */ + * ------------------------------------------------------------------ + */ class ArrayIteratorsTest : public testing::Test { protected: - json::Array m_array; + json::Value m_array{json::Type::Array}; public: ArrayIteratorsTest() @@ -699,68 +725,34 @@ { auto it = m_array.begin(); + // 1 ASSERT_EQ(1, (*it).toInt()); ASSERT_EQ(1, it->toInt()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].toBool()); - - auto it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInt()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].toBool()); - - auto it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); + // "hello" + it++; + ASSERT_EQ("hello", it->toString()); - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); + // true + it++; + ASSERT_TRUE(it->toBool()); } // Read only (const) { auto it = m_array.cbegin(); + // 1 ASSERT_EQ(1, (*it).toInt()); ASSERT_EQ(1, it->toInt()); - ASSERT_EQ("hello", it[1].toString()); - ASSERT_TRUE(it[2].toBool()); - - auto it2 = it + 1; - ASSERT_EQ(1, it2[-1].toInt()); - ASSERT_EQ("hello", it2->toString()); - ASSERT_TRUE(it2[1].toBool()); - - auto it3 = it; - ASSERT_TRUE(it2 != it); - ASSERT_FALSE(it3 != it); - ASSERT_FALSE(it2 == it); - ASSERT_TRUE(it3 == it); - - ASSERT_TRUE(it3 >= it); - ASSERT_TRUE(it2 >= it); + // "hello" + it++; + ASSERT_EQ("hello", it->toString()); - ASSERT_TRUE(it2 > it); - ASSERT_FALSE(it3 > it); - - ASSERT_FALSE(it2 <= it); - ASSERT_TRUE(it3 <= it); - - ASSERT_FALSE(it2 < it); - ASSERT_FALSE(it3 < it); + // true + it++; + ASSERT_TRUE(it->toBool()); } } @@ -780,7 +772,7 @@ TEST_F(ArrayIteratorsTest, castToRef) { - json::Array array{1, 2, 3}; + json::Value array = json::array({1, 2, 3}); int i = 1; for (const json::Value &v : array) {