Mercurial > code
changeset 314:4c3019385769
Json: add iterators
TODO:
- Test operator->, const_iterator must not let modify
- Test const and non const operator[]
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 25 Feb 2015 13:53:41 +0100 |
parents | cd490a8ab82a |
children | c9356cb38c86 b67b2da45bb0 |
files | C++/Json.cpp C++/Json.h C++/Tests/Json/main.cpp |
diffstat | 3 files changed, 1842 insertions(+), 829 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/Json.cpp Wed Feb 25 12:42:06 2015 +0100 +++ b/C++/Json.cpp Wed Feb 25 13:53:41 2015 +0100 @@ -16,177 +16,34 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <stdexcept> + #include "Json.h" /* -------------------------------------------------------- - * JsonValue implementation + * JsonObject * -------------------------------------------------------- */ -JsonValue::JsonValue(const JsonValue &value) - : m_handle{json_deep_copy(value.m_handle.get()), json_decref} -{ -} - -JsonValue &JsonValue::operator=(const JsonValue &value) -{ - m_handle = {json_deep_copy(value.m_handle.get()), json_decref}; -} - -JsonValue::JsonValue(json_t *json) - : m_handle{json, json_decref} -{ -} - -JsonValue::JsonValue() - : m_handle{json_null(), json_decref} -{ -} - -JsonValue::JsonValue(bool value) - : m_handle{json_boolean(value), json_decref} -{ -} - -JsonValue::JsonValue(int value) - : m_handle{json_integer(value), json_decref} -{ -} - -JsonValue::JsonValue(double value) - : m_handle{json_real(value), json_decref} -{ -} - -JsonValue::JsonValue(std::string value) - : m_handle{json_string(value.c_str()), json_decref} -{ -} - -JsonValue::JsonValue(const char *value) - : m_handle{json_string(value), json_decref} -{ -} - -JsonType JsonValue::typeOf() const -{ - return static_cast<JsonType>(json_typeof(m_handle.get())); -} - -bool JsonValue::isObject() const -{ - return json_is_object(m_handle.get()) != 0; -} - -bool JsonValue::isArray() const -{ - return json_is_array(m_handle.get()) != 0; -} - -bool JsonValue::isString() const -{ - return json_is_string(m_handle.get()) != 0; -} - -bool JsonValue::isReal() const -{ - return json_is_real(m_handle.get()) != 0; -} - -bool JsonValue::isTrue() const -{ - return json_is_true(m_handle.get()) != 0; -} - -bool JsonValue::isFalse() const -{ - return json_is_false(m_handle.get()) != 0; -} - -bool JsonValue::isNull() const -{ - return json_is_null(m_handle.get()) != 0; -} - -bool JsonValue::isNumber() const -{ - return json_is_number(m_handle.get()) != 0; -} - -bool JsonValue::isInteger() const -{ - return json_is_integer(m_handle.get()) != 0; -} - -bool JsonValue::isBoolean() const -{ - return json_is_boolean(m_handle.get()) != 0; -} - -std::string JsonValue::toString() const -{ - auto value = json_string_value(m_handle.get()); - - return (value == nullptr) ? "" : value; -} - -int JsonValue::toInteger() const noexcept -{ - return json_integer_value(m_handle.get()); -} - -double JsonValue::toReal() const noexcept -{ - return json_real_value(m_handle.get()); -} - -JsonObject JsonValue::toObject() const +JsonObject JsonValue::toObject() const noexcept { json_incref(m_handle.get()); - return JsonObject{m_handle.get()}; + return JsonObject(m_handle.get()); } -JsonArray JsonValue::toArray() const +JsonArray JsonValue::toArray() const noexcept { json_incref(m_handle.get()); - return JsonArray{m_handle.get()}; + return JsonArray(m_handle.get()); } /* -------------------------------------------------------- * JsonArray * -------------------------------------------------------- */ -JsonArray::JsonArray() - : JsonValue{json_array()} -{ -} - -unsigned JsonArray::size() const noexcept -{ - return json_array_size(m_handle.get()); -} - -void JsonArray::push(const JsonValue &value) +JsonValue JsonArray::at(int index) const { - json_array_insert(m_handle.get(), 0, value.m_handle.get()); -} - -void JsonArray::append(const JsonValue &value) -{ - json_array_append(m_handle.get(), value.m_handle.get()); -} - -void JsonArray::insert(const JsonValue &value, int index) -{ - json_array_insert(m_handle.get(), index, value.m_handle.get()); -} - -JsonValue JsonArray::operator[](int index) const -{ - if (typeOf() != JsonType::Array) - throw JsonError{"not an array"}; - auto value = json_array_get(m_handle.get(), index); if (value == nullptr) @@ -197,66 +54,105 @@ return JsonValue{value}; } +JsonValue JsonArray::operator[](int index) const noexcept +{ + auto value = json_array_get(m_handle.get(), index); + + if (value == nullptr) + return JsonValue(); + + json_incref(value); + + return JsonValue(value); +} + +JsonArray::Ref JsonArray::operator[](int index) noexcept +{ + auto value = json_array_get(m_handle.get(), index); + + if (value == nullptr) + value = json_null(); + else + json_incref(value); + + return Ref(value, *this, index); +} + /* -------------------------------------------------------- * JsonObject * -------------------------------------------------------- */ -JsonObject::JsonObject() - : JsonValue{json_object()} +JsonObject::Ref JsonObject::operator[](const std::string &name) { + if (typeOf() != JsonType::Object) + return Ref(JsonValue(), *this, name); + + auto value = json_object_get(m_handle.get(), name.c_str()); + + json_incref(value); + + return Ref(value, *this, name); } JsonValue JsonObject::operator[](const std::string &name) const { if (typeOf() != JsonType::Object) - throw JsonError{"not an object"}; + return JsonValue(); auto value = json_object_get(m_handle.get(), name.c_str()); if (value == nullptr) - throw JsonError{"key " + name + +" not found"}; + return JsonValue(); json_incref(value); - return JsonValue{value}; -} - -void JsonObject::set(const std::string &key, const JsonValue &value) -{ - json_object_set(m_handle.get(), key.c_str(), value.m_handle.get()); + return JsonValue(value); } /* -------------------------------------------------------- - * JsonReaderFile + * JsonDocument * -------------------------------------------------------- */ -JsonReaderFile::JsonReaderFile(std::string path) - : m_path{std::move(path)} -{ -} - -JsonValue JsonReaderFile::read() +JsonValue JsonDocument::read(std::string content, int flags) const { json_error_t error; - json_t *handle = json_load_file(m_path.c_str(), 0, &error); + json_t *json = json_loads(content.c_str(), flags, &error); - if (handle == nullptr) - throw JsonError{error}; + if (json == nullptr) + throw JsonError(error); - return JsonValue{handle}; + return JsonValue(json); } -/* -------------------------------------------------------- - * JsonWriterFile - * -------------------------------------------------------- */ +JsonValue JsonDocument::read(std::ifstream &stream, int flags) const +{ + if (!stream.is_open()) + throw JsonError("File not opened"); -JsonWriterFile::JsonWriterFile(std::string path) - : m_path{std::move(path)} -{ + stream.seekg(0, stream.end); + auto length = stream.tellg(); + stream.seekg(0, stream.beg); + + std::string buffer; + buffer.resize(length, ' '); + + stream.read(&buffer[0], length); + stream.close(); + + return read(std::move(buffer), flags); } -void JsonWriterFile::write(const JsonValue &value) +JsonDocument::JsonDocument(std::ifstream &stream, int flags) +{ + m_value = read(stream, flags); +} + +JsonDocument::JsonDocument(std::ifstream &&stream, int flags) { - if (json_dump_file(value, m_path.c_str(), 0) < 0) - throw JsonError{"Failed to write file: " + m_path}; + m_value = read(stream, flags); } + +JsonDocument::JsonDocument(std::string content, int flags) +{ + m_value = read(std::move(content), flags); +} \ No newline at end of file
--- a/C++/Json.h Wed Feb 25 12:42:06 2015 +0100 +++ b/C++/Json.h Wed Feb 25 13:53:41 2015 +0100 @@ -19,8 +19,12 @@ #ifndef _JSON_H_ #define _JSON_H_ +#include <algorithm> +#include <cstdlib> +#include <initializer_list> +#include <fstream> +#include <iterator> #include <memory> -#include <stdexcept> #include <string> #include <utility> @@ -56,390 +60,12 @@ Null = JSON_NULL //!< Empty or null }; -class JsonObject; -class JsonArray; - -/** - * @class JsonValue - * @brief Encapsulate any JSON value - */ -class JsonValue { -public: - using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; - - friend class JsonObject; - friend class JsonArray; - friend class JsonDocument; - -protected: - /** - * The unique_ptr handle of json_t, will automatically decrease - * the reference count in its deleter. - */ - Handle m_handle; - -public: - /** - * Create a JsonValue from a native Jansson type. This function - * will increment the json_t reference count. - * - * @param json the value - */ - JsonValue(json_t *json); - - /** - * Deep copy of that element. - * - * @param value the other value - */ - JsonValue(const JsonValue &value); - - /** - * Assign a deep copy of the other element. - * - * @return *this - */ - JsonValue &operator=(const JsonValue &); - - /** - * Default move constructor. - * - * @param other the other value - */ - JsonValue(JsonValue &&other) = default; - - /** - * Default move assignment. - * - * @param other the other value - */ - JsonValue &operator=(JsonValue &&) = default; - - /** - * Create an empty value (JsonType::Null). - */ - JsonValue(); - - /** - * Create a boolean value. - * - * @param value the value - */ - JsonValue(bool value); - - /** - * Create a integer value (JsonType::Integer). - * - * @param value the value - */ - JsonValue(int value); - - /** - * Create a real value (JsonType::Real). - * - * @param value the value - */ - JsonValue(double value); - - /** - * Create a string value (JsonType::String). - * @param value - */ - JsonValue(std::string value); - - /** - * Create from a C string (JsonType::String). - * - * @param value the string - */ - JsonValue(const char *value); - - /** - * Create from a string literal (JsonType::String). - * - * @param value the value - */ - template <size_t Size> - inline JsonValue(char (&value)[Size]) - : m_handle{json_string(value), json_decref} - { - } - - /** - * Default destructor. - */ - virtual ~JsonValue() = default; - - /** - * Get the type of value. - * - * @return the type - */ - JsonType typeOf() const; - - /** - * Tells if the json value is an JSON_OBJECT. - * - * @return true or false - */ - bool isObject() const; - - /** - * Tells if the json value is an JSON_ARRAY. - * - * @return true or false - */ - bool isArray() const; - - /** - * Tells if the json value is an JSON_STRING. - * - * @return true or false - */ - bool isString() const; - - /** - * Tells if the json value is an JSON_REAL. - * - * @return true or false - */ - bool isReal() const; - - /** - * Tells if the json value is an JSON_TRUE. - * - * @return true or false - */ - bool isTrue() const; - - /** - * Tells if the json value is an JSON_FALSE. - * - * @return true or false - */ - bool isFalse() const; - - /** - * Tells if the json value is an JSON_NULL. - * - * @return true or false - */ - bool isNull() const; - - /** - * Tells if the json value is an JSON_INTEGER or JSON_REAL. - * - * @return true or false - */ - bool isNumber() const; - - /** - * Tells if the json value is an JSON_INTEGER. - * - * @return true or false - */ - bool isInteger() const; - - /** - * Tells if the json value is an JSON_TRUE or JSON_FALSE. - * - * @return true or false - */ - bool isBoolean() const; - - /** - * Get the string value. - * - * @return the string - */ - std::string toString() const; - - /** - * Get the integer value. - * - * @return the value or 0 - */ - int toInteger() const noexcept; - - /** - * Get the real value. - * - * @return the value or 0 - */ - double toReal() const noexcept; - - /** - * Convert to object. - * - * @return an object - */ - JsonObject toObject() const; - - /** - * Convert to array. - * - * @return an array - */ - JsonArray toArray() const; - - /** - * Convert to native Jansson type. - * - * You should not call json_incref or json_decref on it as it is - * automatically done. - * - * @return the json_t handle - * @warning use this function with care - */ - inline operator json_t *() noexcept - { - return m_handle.get(); - } - - /** - * Overloaded function. - * - * @return the json_t handle - */ - inline operator const json_t *() const noexcept - { - return m_handle.get(); - } -}; - -/** - * @class JsonArray - * @brief Manipulate JSON arrays - */ -class JsonArray final : public JsonValue { -public: - using JsonValue::JsonValue; - - /** - * Create an empty array. - */ - JsonArray(); - - /** - * Get the number of values in the array - * - * @return the number or 0 - */ - unsigned size() const noexcept; - - /** - * Insert the value at the beginning. - * - * @param value the value - */ - void push(const JsonValue &value); - - /** - * Insert a copy of the value at the end. - * - * @param value the value to insert - */ - void append(const JsonValue &value); - - /** - * Insert a copy of the value at the specified index. - * - * @param value the value to insert - * @param index the position - */ - void insert(const JsonValue &value, int index); - - /** - * Get the value at the specified index. - * - * @param index the position - * @return the value - * @throw std::invalid_argument on error - * @throw std::out_of_range on bad arguments - */ - JsonValue operator[](int index) const; - - /** - * Iterate over the array value (not recursively). - * - * The function must have the following signature: - * void f(int, const JsonValue &) - * - * @param function the function to call - */ - template <typename Func> - void forAll(Func function) const - { - json_t *value; - size_t index; - - json_array_foreach(m_handle.get(), index, value) { - json_incref(value); - function(static_cast<int>(index), JsonValue{value}); - } - } -}; - -/** - * @class JsonObject - * @brief Object wrapper - */ -class JsonObject final : public JsonValue { -public: - using JsonValue::JsonValue; - - /** - * Create empty object. - */ - JsonObject(); - - /** - * Get the value at the specified key. The returned value is a borrowed reference, - * - * @param key the key - * @return the value - * @throw std::invalid_argument on error - * @throw std::out_of_range on bad arguments - */ - JsonValue operator[](const std::string &key) const; - - /** - * Set the value as key in the object. - * - * @param key the key - * @param value the value - */ - void set(const std::string &key, const JsonValue &value); - - /** - * Iterate over the object keys (not recursively). - * - * The function must have the following signature: - * void f(const std::string &key, const JsonValue &) - * - * @param function the function to call - */ - template <typename Func> - void forAll(Func function) const - { - json_t *value; - const char *key; - - json_object_foreach(m_handle.get(), key, value) { - json_incref(value); - function(std::string(key), JsonValue{value}); - } - } -}; - /** * @class JsonError * @brief Error thrown for any error */ class JsonError final : public std::exception { private: - friend class Json; - std::string m_text; std::string m_source; int m_line{}; @@ -532,104 +158,1312 @@ } }; +class JsonObject; +class JsonArray; + /** - * @class JsonReader - * @brief Base class for JSON reading + * @class JsonValue + * @brief Encapsulate any JSON value */ -class JsonReader { +class JsonValue { +public: + using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; + + friend class JsonObject; + friend class JsonArray; + +protected: + /** + * The unique_ptr handle of json_t, will automatically decrease + * the reference count in its deleter. + */ + Handle m_handle; + public: /** - * Default constructor. + * Create a JsonValue from a native Jansson type. This function + * will increment the json_t reference count. + * + * @param json the value */ - JsonReader() = default; + inline JsonValue(json_t *json) noexcept + : m_handle{json, json_decref} + { + } /** - * Default destructor. + * Deep copy of that element. + * + * @param value the other value + */ + inline JsonValue(const JsonValue &value) + : m_handle{json_deep_copy(value.m_handle.get()), json_decref} + { + } + + /** + * Assign a deep copy of the other element. + * + * @return *this */ - virtual ~JsonReader() = default; + inline JsonValue &operator=(const JsonValue &value) + { + m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref); + + return *this; + } + + /** + * Default move constructor. + * + * @param other the other value + */ + JsonValue(JsonValue &&other) = default; /** - * Read the source and return a value. + * Default move assignment. + * + * @param other the other value + */ + JsonValue &operator=(JsonValue &&) = default; + + /** + * Create an empty value (JsonType::Null). + */ + inline JsonValue() + : m_handle(json_null(), json_decref) + { + } + + /** + * Create a boolean value. * - * @return a value + * @param value the value */ - virtual JsonValue read() = 0; -}; + inline JsonValue(bool value) + : m_handle(json_boolean(value), json_decref) + { + } + + /** + * Create a integer value (JsonType::Integer). + * + * @param value the value + */ + inline JsonValue(int value) + : m_handle(json_integer(value), json_decref) + { + } -/** - * @class JsonWriter - * @brief Base class for writing - */ -class JsonWriter { -public: + /** + * Create a real value (JsonType::Real). + * + * @param value the value + */ + inline JsonValue(double value) + : m_handle(json_real(value), json_decref) + { + } + + /** + * Create a string value (JsonType::String). + * @param value + */ + inline JsonValue(std::string value) + : m_handle(json_string(value.c_str()), json_decref) + { + } + /** - * Default destructor. + * Create from a C string (JsonType::String). + * + * @param value the string */ - JsonWriter() = default; + inline JsonValue(const char *value) + : m_handle(json_string(value), json_decref) + { + } + + /** + * Create from a string literal (JsonType::String). + * + * @param value the value + */ + template <size_t Size> + inline JsonValue(char (&value)[Size]) + : m_handle{json_string(value), json_decref} + { + } /** * Default destructor. */ - virtual ~JsonWriter() = default; + virtual ~JsonValue() = default; + + /** + * Get the type of value. + * + * @return the type + */ + inline JsonType typeOf() const noexcept + { + return static_cast<JsonType>(json_typeof(m_handle.get())); + } + + /** + * Tells if the json value is an JSON_OBJECT. + * + * @return true or false + */ + inline bool isObject() const noexcept + { + return json_is_object(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_ARRAY. + * + * @return true or false + */ + inline bool isArray() const noexcept + { + return json_is_array(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_STRING. + * + * @return true or false + */ + inline bool isString() const noexcept + { + return json_is_string(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_REAL. + * + * @return true or false + */ + inline bool isReal() const noexcept + { + return json_is_real(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_TRUE. + * + * @return true or false + */ + inline bool isTrue() const noexcept + { + return json_is_true(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_FALSE. + * + * @return true or false + */ + inline bool isFalse() const noexcept + { + return json_is_false(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_NULL. + * + * @return true or false + */ + inline bool isNull() const noexcept + { + return json_is_null(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_INTEGER or JSON_REAL. + * + * @return true or false + */ + inline bool isNumber() const noexcept + { + return json_is_number(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_INTEGER. + * + * @return true or false + */ + inline bool isInteger() const noexcept + { + return json_is_integer(m_handle.get()); + } + + /** + * Tells if the json value is an JSON_TRUE or JSON_FALSE. + * + * @return true or false + */ + inline bool isBoolean() const noexcept + { + return json_is_boolean(m_handle.get()); + } /** - * Write to the source the value. + * Get the string value. + * + * @return the string + */ + inline std::string toString() const noexcept + { + auto value = json_string_value(m_handle.get()); + + return (value == nullptr) ? "" : value; + } + + /** + * Get the integer value. + * + * @return the value or 0 + */ + inline int toInteger() const noexcept + { + return json_integer_value(m_handle.get()); + } + + /** + * Get the real value. + * + * @return the value or 0 + */ + inline double toReal() const noexcept + { + return json_real_value(m_handle.get()); + } + + /** + * Convert to object. + * + * @return an object + */ + JsonObject toObject() const noexcept; + + /** + * Convert to array. + * + * @return an array + */ + JsonArray toArray() const noexcept; + + /** + * Write to a stream. * - * @param value the value + * @param out the out + * @param flags the optional Jansson flags + */ + inline void write(std::ofstream &out, int flags = 0) const + { + auto content = dump(flags); + + std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator<char>(out)); + } + + /** + * Overloaded function. + * + * @param out the out + * @param flags the optional Jansson flags + */ + inline void write(std::ofstream &&out, int flags = 0) const + { + write(out, flags); + } + + /** + * Convert the Json value as a string. + * + * @return the string + * @param flags the optional Jansson flags */ - virtual void write(const JsonValue &value) = 0; + inline std::string dump(int flags = 0) const + { + auto str = json_dumps(m_handle.get(), flags); + + if (str == nullptr) + return ""; + + std::string ret(str); + std::free(str); + + return ret; + } + + /** + * Convert to native Jansson type. + * + * You should not call json_incref or json_decref on it as it is + * automatically done. + * + * @return the json_t handle + * @warning use this function with care + */ + inline operator json_t *() noexcept + { + return m_handle.get(); + } + + /** + * Overloaded function. + * + * @return the json_t handle + */ + inline operator const json_t *() const noexcept + { + return m_handle.get(); + } + + /** + * Equality operator. + */ + inline bool operator==(const JsonValue &other) const noexcept + { + return json_equal(m_handle.get(), other.m_handle.get()); + } }; /** - * @class JsonReaderFile - * @brief Read JSON data from a file + * @class JsonArray + * @brief Manipulate JSON arrays */ -class JsonReaderFile final : public JsonReader { -private: - std::string m_path; +class JsonArray final : public JsonValue { +public: + /** + * @class Ref + * @brief Reference wrapper to be assignable + */ + class Ref final : public JsonValue { + private: + JsonArray &m_array; + int m_index; + + public: + explicit inline Ref(JsonValue value, JsonArray &array, int index) + : JsonValue(std::move(value)) + , m_array(array) + , m_index(index) + { + } + + inline operator JsonValue() const noexcept + { + return *this; + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + + inline Ref &operator=(const JsonValue &value) + { + m_array.replace(value, m_index); + + return *this; + } + + inline Ref &operator=(JsonValue &&value) + { + m_array.replace(std::move(value), m_index); + + return *this; + } + }; + + /** + * @class Ptr + * @brief Pointer wrapper for JsonValue iterators + */ + class Ptr final : public JsonValue { + public: + explicit Ptr(JsonValue value) noexcept + : JsonValue(std::move(value)) + { + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + }; + + class iterator final { + public: + using iterator_category = std::random_access_iterator_tag; + using difference_type = int; + using value_type = JsonValue; + using reference = Ref; + using pointer = Ptr; + + private: + JsonArray &m_array; + int m_index; + + public: + explicit inline iterator(JsonArray &array, int index = 0) noexcept + : m_array(array) + , m_index(index) + { + } + + inline Ref operator*() const + { + return Ref(m_array.at(m_index), m_array, m_index); + } + + inline Ptr operator->() const + { + return Ptr(m_array.at(m_index)); + } + + inline Ref operator[](int nindex) const noexcept + { + return Ref(m_array.at(m_index + nindex), m_array, m_index + nindex); + } + + inline bool operator==(const iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline bool operator<(const iterator &other) const noexcept + { + return m_index < other.m_index; + } + + inline bool operator<=(const iterator &other) const noexcept + { + return m_index <= other.m_index; + } + + inline bool operator>(const iterator &other) const noexcept + { + return m_index > other.m_index; + } + + inline bool operator>=(const iterator &other) const noexcept + { + return m_index >= other.m_index; + } + + inline iterator &operator++() noexcept + { + ++m_index; + + return *this; + } + + inline iterator operator++(int) noexcept + { + iterator save = *this; + + ++m_index; + + return save; + } + + inline iterator &operator--() noexcept + { + m_index--; + + return *this; + } + + inline iterator operator--(int) noexcept + { + iterator save = *this; + + m_index--; + + return save; + } + + inline iterator &operator+=(int nindex) noexcept + { + m_index += nindex; + + return *this; + } + + inline iterator &operator-=(int nindex) noexcept + { + m_index -= nindex; + + return *this; + } + + inline iterator operator+(int nindex) const noexcept + { + return iterator(m_array, m_index + nindex); + } + + inline iterator operator-(int nindex) const noexcept + { + return iterator(m_array, m_index - nindex); + } + + inline int operator-(iterator other) const noexcept + { + return m_index - other.m_index; + } + }; + + class const_iterator final { + public: + using iterator_category = std::random_access_iterator_tag; + using difference_type = int; + using value_type = JsonValue; + using reference = JsonValue; + using pointer = Ptr; + + private: + const JsonArray &m_array; + int m_index; + + public: + explicit inline const_iterator(const JsonArray &array, int index = 0) noexcept + : m_array(array) + , m_index(index) + { + } + + inline JsonValue operator*() const + { + return m_array.at(m_index); + } + + inline Ptr operator->() const + { + return Ptr(m_array.at(m_index)); + } + + inline JsonValue operator[](int nindex) const noexcept + { + return m_array.at(m_index + nindex); + } + + inline bool operator==(const const_iterator &other) const noexcept + { + return m_index == other.m_index; + } + + inline bool operator!=(const const_iterator &other) const noexcept + { + return m_index != other.m_index; + } + + inline bool operator<(const const_iterator &other) const noexcept + { + return m_index < other.m_index; + } + + inline bool operator<=(const const_iterator &other) const noexcept + { + return m_index <= other.m_index; + } + + inline bool operator>(const const_iterator &other) const noexcept + { + return m_index > other.m_index; + } + + inline bool operator>=(const const_iterator &other) const noexcept + { + return m_index >= other.m_index; + } + + inline const_iterator &operator++() noexcept + { + ++m_index; + + return *this; + } + + inline const_iterator operator++(int) noexcept + { + const_iterator save = *this; + + ++m_index; + + return save; + } + + inline const_iterator &operator--() noexcept + { + m_index--; + + return *this; + } + + inline const_iterator operator--(int) noexcept + { + const_iterator save = *this; + + m_index--; + + return save; + } + + inline const_iterator &operator+=(int nindex) noexcept + { + m_index += nindex; + + return *this; + } + + inline const_iterator &operator-=(int nindex) noexcept + { + m_index -= nindex; + + return *this; + } + + inline const_iterator operator+(int nindex) const noexcept + { + return const_iterator(m_array, m_index + nindex); + } + + inline const_iterator operator-(int nindex) const noexcept + { + return const_iterator(m_array, m_index - nindex); + } + + inline int operator-(const_iterator other) const noexcept + { + return m_index - other.m_index; + } + }; + + using size_type = int; + using value_type = JsonValue; + using const_pointer = const value_type *; + using reference = JsonValue &; + using const_reference = const JsonValue &; + using difference_type = int; + +protected: + using JsonValue::JsonValue; public: /** - * Construct a JsonReader to a file. This function does not open - * the file immediately. + * Create an empty array. + */ + inline JsonArray() + : JsonValue(json_array()) + { + } + + /** + * Create an array from a list of values. + * + * @param list the list + */ + inline JsonArray(std::initializer_list<value_type> list) + : JsonArray() + { + for (auto &v : list) + append(std::move(v)); + } + + /** + * Returns an iterator to the beginning. + * + * @return the iterator + */ + inline iterator begin() noexcept + { + return iterator(*this, 0); + } + + /** + * Overloaded function. * - * @param path the path + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + return const_iterator(*this, 0); + } + + /** + * Overloaded function. + * + * @return the iterator */ - JsonReaderFile(std::string path); + inline const_iterator cbegin() const noexcept + { + return const_iterator(*this, 0); + } + + /** + * Returns an iterator to the end. + * + * @return the iterator + */ + inline iterator end() noexcept + { + return iterator(*this, size()); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator end() const noexcept + { + return const_iterator(*this, size()); + } /** - * Read the file and extract a JsonValue. + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + return const_iterator(*this, size()); + } + + /** + * Get a value. + * + * @param index the index + * @throw JsonError on error + */ + JsonValue at(int index) const; + + /** + * Get the number of values in the array + * + * @return the number or 0 + */ + inline unsigned size() const noexcept + { + return json_array_size(m_handle.get()); + } + + /** + * Insert the value at the beginning. + * + * @param value the value + */ + inline void push(const JsonValue &value) noexcept + { + json_array_insert(m_handle.get(), 0, value.m_handle.get()); + } + + /** + * Insert a copy of the value at the end. * - * @return a JsonValue (which will be JsonType::Object or JsonType::Array) - * @throw JsonError on errors + * @param value the value to insert + */ + inline void append(const JsonValue &value) + { + json_array_append(m_handle.get(), value.m_handle.get()); + } + + /** + * Insert a copy of the value at the specified index. + * + * @param value the value to insert + * @param index the position */ - JsonValue read() override; + inline void insert(const JsonValue &value, int index) + { + json_array_insert(m_handle.get(), index, value.m_handle.get()); + } + + /** + * Replace the value at the given index. + * + * @param value the value + * @param index the index + */ + inline void replace(const JsonValue &value, int index) + { + json_array_set(m_handle.get(), index, value.m_handle.get()); + } + + /** + * Get the value at the specified index. + * + * @param index the position + * @return the value + */ + JsonValue operator[](int index) const noexcept; + + /** + * Access a value as a reference wrapper. + * + * @param index the position + * @return the reference wrapper over the value + */ + Ref operator[](int index) noexcept; }; /** - * @class JsonWriterFile - * @brief Write JSON data to file + * @class JsonObject + * @brief Object wrapper */ -class JsonWriterFile final : public JsonWriter { -private: - std::string m_path; +class JsonObject final : public JsonValue { +public: + using key_type = std::string; + using mapped_type = JsonValue; + using size_type = int; + using value_type = std::pair<key_type, mapped_type>; + using const_pointer = const value_type *; + using reference = JsonValue &; + using const_reference = const JsonValue &; + using difference_type = int; + + /** + * @class Ref + * @brief Wrapper for updating JsonObject + * + * This class is only used for the following functions: + * + * JsonObject::operator[] + */ + class Ref final : public JsonValue { + private: + JsonObject &m_object; + std::string m_key; + + public: + explicit inline Ref(JsonValue value, JsonObject &object, std::string key) + : JsonValue(std::move(value)) + , m_object(object) + , m_key(std::move(key)) + { + } + + inline operator JsonValue() const noexcept + { + return *this; + } + + inline JsonValue &operator*() noexcept + { + return *this; + } + + inline JsonValue *operator->() noexcept + { + return this; + } + + inline Ref &operator=(const JsonValue &value) + { + m_object.set(m_key, value); + + return *this; + } + + inline Ref &operator=(JsonValue &&value) + { + m_object.set(m_key, std::move(value)); + + return *this; + } + }; + + /** + * @class Ptr + * @brief Pointer wrapper for JsonValue iterators + * + * For const iterators, the real type is a JsonValue, for non const + * iterators it's a ref so that user can edit it. + */ + template <typename Type> + class Ptr final { + private: + std::pair<std::string, Type> m_pair; + + public: + inline Ptr(std::pair<std::string, Type> value) noexcept + : m_pair(std::move(value)) + { + } + + inline std::pair<std::string, Type> &operator*() noexcept + { + return m_pair; + } + + inline std::pair<std::string, Type> *operator->() noexcept + { + return &m_pair; + } + }; + + /** + * @class iterator + * @brief Forward iterator + */ + class iterator { + public: + using value_type = std::pair<std::string, Ref>; + + private: + JsonObject &m_object; + void *m_keyIt; + + inline std::string key() const noexcept + { + return json_object_iter_key(m_keyIt); + } + + public: + explicit inline iterator(JsonObject &object, void *keyIt) noexcept + : m_object(object) + , m_keyIt(keyIt) + { + } + + inline value_type operator*() const + { + auto k = key(); + + return value_type(k, Ref(m_object[k], m_object, k)); + } + + inline Ptr<Ref> operator->() const + { + auto k = key(); + + return Ptr<Ref>({k, Ref(m_object[k], m_object, k)}); + } + + inline iterator &operator++() noexcept + { + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return *this; + } + + inline iterator operator++(int) noexcept + { + iterator save = *this; + + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return save; + } + + inline operator==(iterator other) const noexcept + { + return m_keyIt == other.m_keyIt; + } + + inline operator!=(iterator other) const noexcept + { + return m_keyIt != other.m_keyIt; + } + }; + + /** + * @class const_iterator + * @brief Forward iterator + */ + class const_iterator { + public: + using value_type = std::pair<std::string, JsonValue>; + + private: + const JsonObject &m_object; + void *m_keyIt; + + inline std::string key() const noexcept + { + return json_object_iter_key(m_keyIt); + } + + public: + explicit inline const_iterator(const JsonObject &object, void *keyIt) noexcept + : m_object(object) + , m_keyIt(keyIt) + { + } + + inline value_type operator*() const + { + auto k = key(); + + return value_type(k, m_object[k]); + } + + inline Ptr<JsonValue> operator->() const + { + auto k = key(); + + return Ptr<JsonValue>({k, m_object[k]}); + } + + inline const_iterator &operator++() noexcept + { + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return *this; + } + + inline const_iterator operator++(int) noexcept + { + const_iterator save = *this; + + m_keyIt = json_object_iter_next(m_object.m_handle.get(), m_keyIt); + + return save; + } + + inline operator==(const_iterator other) const noexcept + { + return m_keyIt == other.m_keyIt; + } + + inline operator!=(const_iterator other) const noexcept + { + return m_keyIt != other.m_keyIt; + } + }; + +protected: + using JsonValue::JsonValue; public: /** - * Construct a JsonReader to a file. This function does not open - * the file immediately. + * Create empty object. + */ + inline JsonObject() + : JsonValue(json_object()) + { + } + + /** + * Create a JsonObject from an initializer_list. + * + * @param list the list of key-value pairs + */ + inline JsonObject(std::initializer_list<value_type> list) + : JsonObject() + { + for (auto &v : list) + set(v.first, std::move(v.second)); + } + + /** + * Returns an iterator to the beginning. + * + * @return the iterator + */ + inline iterator begin() noexcept + { + return iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator begin() const noexcept + { + return const_iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Overloaded function. * - * @param path + * @return the iterator + */ + inline const_iterator cbegin() const noexcept + { + return const_iterator(*this, json_object_iter(m_handle.get())); + } + + /** + * Returns an iterator to the end. + * + * @return the iterator + */ + inline iterator end() noexcept + { + return iterator(*this, nullptr); + } + + /** + * Overloaded function. + * + * @return the iterator */ - JsonWriterFile(std::string path); + inline const_iterator end() const noexcept + { + return const_iterator(*this, nullptr); + } + + /** + * Overloaded function. + * + * @return the iterator + */ + inline const_iterator cend() const noexcept + { + return const_iterator(*this, nullptr); + } + + /** + * Check if the object contains a specific property. + * + * @param key the key + * @return true if the object contains the key + */ + inline bool contains(const std::string &key) const noexcept + { + return json_object_get(m_handle.get(), key.c_str()) != nullptr; + } + + /** + * Get the number of items in the object. + * + * @return the number of items + */ + inline unsigned size() const noexcept + { + return json_object_size(m_handle.get()); + } /** - * Write to the file. + * Set the value as key in the object. + * + * @param key the key + * @param value the value + */ + inline void set(const std::string &key, const JsonValue &value) noexcept + { + json_object_set(m_handle.get(), key.c_str(), value.m_handle.get()); + } + + /** + * Access an object as a wrapper so that you can update its content + * with convenience. + * + * @param key the key + */ + Ref operator[](const std::string &key); + + /** + * Get the value at the specified key. If the value is not found, an + * empty value is returned * - * @param value the value + * @param key the key + * @return the value + */ + JsonValue operator[](const std::string &key) const; +}; + +/** + * @class JsonDocument + * @brief Read files and strings to create Json values + */ +class JsonDocument final { +private: + JsonValue m_value; + + JsonValue read(std::string, int flags) const; + JsonValue read(std::ifstream &stream, int flags) const; + +public: + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags * @throw JsonError on errors */ - void write(const JsonValue &value) override; + JsonDocument(std::ifstream &fstream, int flags = 0); + + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags + * @throw JsonError on errors + */ + JsonDocument(std::ifstream &&stream, int flags = 0); + + /** + * Construct a document from a file. + * + * @param stream the stream + * @param flags the optional Jansson flags + * @throw JsonError on errors + */ + JsonDocument(std::string content, int flags = 0); + + /** + * Check if the document contains an object. + * + * @return true if object + */ + inline bool isObject() const noexcept + { + return m_value.isObject(); + } + + /** + * Check if the document contains an array. + * + * @return true if array + */ + inline bool isArray() const noexcept + { + return m_value.isArray(); + } + + /** + * Convert the document value to object + * + * @return the value as object + */ + inline JsonObject toObject() const noexcept + { + return m_value.toObject(); + } + + /** + * Convert the document value to array + * + * @return the value as array + */ + inline JsonArray toArray() const noexcept + { + return m_value.toArray(); + } }; #endif // !_JSON_H_
--- a/C++/Tests/Json/main.cpp Wed Feb 25 12:42:06 2015 +0100 +++ b/C++/Tests/Json/main.cpp Wed Feb 25 13:53:41 2015 +0100 @@ -16,6 +16,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <set> +#include <unordered_map> + #include <gtest/gtest.h> #include "Json.h" @@ -125,159 +128,9 @@ } /* -------------------------------------------------------- - * I/O - * -------------------------------------------------------- */ - -TEST(Open, simple) -{ - try { - JsonReaderFile reader{SOURCE "/data/simple.json"}; - - (void)reader.read(); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Write, simpleArray) -{ - try { - // Write - { - JsonArray array; - - array.append(123); - array.append(true); - - JsonWriterFile{BINARY "/write-array.json"}.write(array); - } - - // Read - { - JsonArray array = JsonReaderFile{BINARY "/write-array.json"}.read().toArray(); - - ASSERT_TRUE(array.isArray()); - ASSERT_EQ(123, array[0].toInteger()); - ASSERT_TRUE(array[1].isTrue()); - } - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Write, simpleObject) -{ - try { - // Write - { - JsonObject object; - - object.set("integer", 123); - object.set("true", true); - - JsonWriterFile{BINARY "/write-object.json"}.write(object); - } - - // Read - { - JsonObject object = JsonReaderFile{BINARY "/write-object.json"}.read().toObject(); - - ASSERT_TRUE(object.isObject()); - ASSERT_EQ(123, object["integer"].toInteger()); - ASSERT_TRUE(object["true"]); - } - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- * Object * -------------------------------------------------------- */ -TEST(Object, simple) -{ - try { - JsonValue value = JsonReaderFile{SOURCE "/data/object.json"}.read(); - - ASSERT_TRUE(value.isObject()); - ASSERT_FALSE(value.isArray()); - - JsonObject object = value.toObject(); - - ASSERT_TRUE(object.isObject()); - - JsonValue name = object["name"]; - JsonValue description = object["description"]; - - ASSERT_TRUE(name.isString()); - ASSERT_TRUE(description.isString()); - ASSERT_EQ("simple", name.toString()); - ASSERT_EQ("basic JSON file", description.toString()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Object, all) -{ - try { - JsonObject object = JsonReaderFile{SOURCE "/data/object-all.json"}.read().toObject(); - - ASSERT_TRUE(object["integer"].isInteger()); - ASSERT_TRUE(object["integer"].isNumber()); - ASSERT_EQ(123, object["integer"].toInteger()); - - ASSERT_TRUE(object["real"].isReal()); - ASSERT_TRUE(object["real"].isNumber()); - ASSERT_EQ(9.2, object["real"].toReal()); - - ASSERT_TRUE(object["false"].isBoolean()); - ASSERT_TRUE(object["false"].isFalse()); - ASSERT_FALSE(object["false"].isTrue()); - - ASSERT_TRUE(object["true"].isBoolean()); - ASSERT_TRUE(object["true"].isTrue()); - ASSERT_FALSE(object["true"].isFalse()); - - ASSERT_TRUE(object["null"].isNull()); - - ASSERT_TRUE(object["object"].isObject()); - ASSERT_TRUE(object["array"].isArray()); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - -TEST(Object, forAll) -{ - try { - JsonObject object = JsonReaderFile{SOURCE "/data/object-all.json"}.read().toObject(); - - object.forAll([] (const auto &key, const auto &value) { - if (key == "integer") { - ASSERT_TRUE(value.isInteger()); - ASSERT_EQ(123, value.toInteger()); - } else if (key == "real") { - ASSERT_TRUE(value.isReal()); - ASSERT_EQ(9.2, value.toReal()); - } else if (key == "false") { - ASSERT_TRUE(value.isFalse()); - } else if (key == "true") { - ASSERT_TRUE(value.isTrue()); - } else if (key == "null") { - ASSERT_TRUE(value.isNull()); - } else if (key == "object") { - ASSERT_TRUE(value.isObject()); - } else if (key == "array") { - ASSERT_TRUE(value.isArray()); - } - }); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} - TEST(Object, set) { try { @@ -295,89 +148,54 @@ } } -/* -------------------------------------------------------- - * Array - * -------------------------------------------------------- */ - -TEST(Array, simple) +TEST(ObjectInitializer, simple) { try { - JsonValue value = JsonReaderFile{SOURCE "/data/array.json"}.read(); - - ASSERT_TRUE(value.isArray()); - ASSERT_FALSE(value.isObject()); - - JsonArray array = value.toArray(); + JsonObject object{ + { "username", "jean" }, + { "age", 99 } + }; - ASSERT_TRUE(array.isArray()); - - ASSERT_EQ(3, array.size()); - ASSERT_EQ(1, array[0].toInteger()); - ASSERT_EQ(2, array[1].toInteger()); - ASSERT_EQ(3, array[2].toInteger()); - } catch (const JsonError &ex) { + ASSERT_EQ(2, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInteger()); + } catch (const std::exception &ex) { FAIL() << ex.what(); } } -TEST(Array, all) +TEST(ObjectInitializer, deep) { try { - JsonArray array = JsonReaderFile{SOURCE "/data/array-all.json"}.read().toArray(); - - ASSERT_TRUE(array[0].isInteger()); - ASSERT_TRUE(array[0].isNumber()); - ASSERT_EQ(123, array[0].toInteger()); - - ASSERT_TRUE(array[1].isReal()); - ASSERT_TRUE(array[1].isNumber()); - ASSERT_EQ(9.2, array[1].toReal()); + JsonObject object{ + { "username", "jean" }, + { "age", 99 }, + { "network", JsonObject{ + { "port", 9999 }, + { "host", "localhost" } + } + } + }; - ASSERT_TRUE(array[2].isBoolean()); - ASSERT_TRUE(array[2].isFalse()); - ASSERT_FALSE(array[2].isTrue()); + // First + ASSERT_EQ(3, static_cast<int>(object.size())); + ASSERT_EQ("jean", object["username"].toString()); + ASSERT_EQ(99, object["age"].toInteger()); - ASSERT_TRUE(array[3].isBoolean()); - ASSERT_TRUE(array[3].isTrue()); - ASSERT_FALSE(array[3].isFalse()); - - ASSERT_TRUE(array[4].isNull()); - - ASSERT_TRUE(array[5].isObject()); - ASSERT_TRUE(array[6].isArray()); - } catch (const JsonError &ex) { + // Second + JsonObject network = object["network"].toObject(); + ASSERT_TRUE(network.isObject()); + ASSERT_EQ(2, static_cast<int>(network.size())); + ASSERT_EQ(9999, network["port"].toInteger()); + ASSERT_EQ("localhost", network["host"].toString()); + } catch (const std::exception &ex) { FAIL() << ex.what(); } } -TEST(Array, forAll) -{ - try { - JsonArray array = JsonReaderFile{SOURCE "/data/array-all.json"}.read().toArray(); - - array.forAll([] (auto index, const auto &value) { - if (index == 0) { - ASSERT_TRUE(value.isInteger()); - ASSERT_EQ(123, value.toInteger()); - } else if (index == 1) { - ASSERT_TRUE(value.isReal()); - ASSERT_EQ(9.2, value.toReal()); - } else if (index == 2) { - ASSERT_TRUE(value.isFalse()); - } else if (index == 3) { - ASSERT_TRUE(value.isTrue()); - } else if (index == 4) { - ASSERT_TRUE(value.isNull()); - } else if (index == 5) { - ASSERT_TRUE(value.isObject()); - } else if (index == 6) { - ASSERT_TRUE(value.isArray()); - } - }); - } catch (const JsonError &ex) { - FAIL() << ex.what(); - } -} +/* -------------------------------------------------------- + * Array + * -------------------------------------------------------- */ TEST(Array, push) { @@ -390,7 +208,7 @@ array.push("hello"); array.push(true); - ASSERT_EQ(3, array.size()); + ASSERT_EQ(3, static_cast<int>(array.size())); ASSERT_TRUE(array[0].isTrue()); ASSERT_EQ("hello", array[1].toString()); ASSERT_EQ(1, array[2].toInteger()); @@ -410,7 +228,7 @@ array.append("hello"); array.append(true); - ASSERT_EQ(3, array.size()); + ASSERT_EQ(3, static_cast<int>(array.size())); ASSERT_EQ(1, array[0].toInteger()); ASSERT_EQ("hello", array[1].toString()); ASSERT_TRUE(array[2].isTrue()); @@ -430,7 +248,7 @@ array.insert("hello", 1); array.insert(true, 0); - ASSERT_EQ(3, array.size()); + ASSERT_EQ(3, static_cast<int>(array.size())); ASSERT_TRUE(array[0].isTrue()); ASSERT_EQ(1, array[1].toInteger()); ASSERT_EQ("hello", array[2].toString()); @@ -439,6 +257,471 @@ } } +TEST(ArrayInitializer, simple) +{ + try { + JsonArray array{123, true, "hello"}; + + ASSERT_EQ(3, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInteger()); + ASSERT_TRUE(array[1].isTrue()); + ASSERT_EQ("hello", array[2].toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(ArrayInitializer, deep) +{ + try { + JsonArray array{ + 123, + true, + "hello", + JsonArray{ + 321, + false, + "olleh" + } + }; + + // First + ASSERT_EQ(4, static_cast<int>(array.size())); + ASSERT_EQ(123, array[0].toInteger()); + ASSERT_TRUE(array[1].isTrue()); + ASSERT_EQ("hello", array[2].toString()); + + // Second + JsonArray array2 = array[3].toArray(); + ASSERT_TRUE(array.isArray()); + ASSERT_EQ(3, static_cast<int>(array2.size())); + ASSERT_EQ(321, array2[0].toInteger()); + ASSERT_TRUE(array2[1].isFalse()); + ASSERT_EQ("olleh", array2[2].toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * I/O + * -------------------------------------------------------- */ + +TEST(FileRead, simple) +{ + try { + JsonDocument doc(std::ifstream(SOURCE "/data/simple.json")); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + + JsonObject object = doc.toObject(); + JsonArray array = doc.toArray(); + + ASSERT_TRUE(object.isObject()); + ASSERT_FALSE(array.isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(FileRead, fail) +{ + try { + JsonDocument(std::ifstream(SOURCE "/data/notexist.json")); + + FAIL() << "Exception expected"; + } catch (const JsonError &) { + } +} + +TEST(FileWrite, simple) +{ + try { + JsonObject object{ + { "name", "jean" }, + { "age", 99 } + }; + + object.write(std::ofstream(BINARY "/object-write.json")); + + JsonObject object2 = JsonDocument(std::ifstream(BINARY "/object-write.json")).toObject(); + + ASSERT_EQ(object2, object); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(StringRead, simple) +{ + try { + JsonDocument doc("{ \"license\": \"ISC\" }"); + + ASSERT_TRUE(doc.isObject()); + ASSERT_FALSE(doc.isArray()); + + JsonObject object = doc.toObject(); + JsonArray array = doc.toArray(); + + ASSERT_TRUE(object.isObject()); + ASSERT_FALSE(array.isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST(StringRead, fail) +{ + try { + JsonDocument("{ \"license\": ISC }"); + + FAIL() << "Exception expected"; + } catch (const JsonError &ex) { + } +} + +TEST(StringWrite, simple) +{ + try { + JsonObject object{ + { "name", "jean" }, + { "age", 99 } + }; + + JsonObject object2 = JsonDocument(object.dump()).toObject(); + + ASSERT_EQ(object2, object); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Object read + * -------------------------------------------------------- */ + +class ObjectRead : public testing::Test { +protected: + JsonObject m_object; + JsonObject m_objectAll; + +public: + ObjectRead() + { + m_object = JsonDocument(std::ifstream(SOURCE "/data/object.json")).toObject(); + m_objectAll = JsonDocument(std::ifstream(SOURCE "/data/object-all.json")).toObject(); + } +}; + +TEST_F(ObjectRead, simple) +{ + try { + JsonValue name = m_object["name"]; + JsonValue description = m_object["description"]; + + ASSERT_TRUE(name.isString()); + ASSERT_TRUE(description.isString()); + ASSERT_EQ("simple", name.toString()); + ASSERT_EQ("basic JSON file", description.toString()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ObjectRead, all) +{ + try { + ASSERT_TRUE(m_objectAll.contains("integer")); + ASSERT_FALSE(m_objectAll.contains("unexistant")); + + ASSERT_TRUE(m_objectAll["integer"].isInteger()); + ASSERT_TRUE(m_objectAll["integer"].isNumber()); + ASSERT_EQ(123, m_objectAll["integer"].toInteger()); + + ASSERT_TRUE(m_objectAll["real"].isReal()); + ASSERT_TRUE(m_objectAll["real"].isNumber()); + ASSERT_EQ(9.2, m_objectAll["real"].toReal()); + + ASSERT_TRUE(m_objectAll["false"].isBoolean()); + ASSERT_TRUE(m_objectAll["false"].isFalse()); + ASSERT_FALSE(m_objectAll["false"].isTrue()); + + ASSERT_TRUE(m_objectAll["true"].isBoolean()); + ASSERT_TRUE(m_objectAll["true"].isTrue()); + ASSERT_FALSE(m_objectAll["true"].isFalse()); + + ASSERT_TRUE(m_objectAll["null"].isNull()); + + ASSERT_TRUE(m_objectAll["object"].isObject()); + ASSERT_TRUE(m_objectAll["array"].isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Array read + * -------------------------------------------------------- */ + +class ArrayRead : public testing::Test { +protected: + JsonArray m_array; + JsonArray m_arrayAll; + +public: + ArrayRead() + { + m_array = JsonDocument(std::ifstream(SOURCE "/data/array.json")).toArray(); + m_arrayAll = JsonDocument(std::ifstream(SOURCE "/data/array-all.json")).toArray(); + } +}; + +TEST_F(ArrayRead, simple) +{ + try { + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(1, m_array[0].toInteger()); + ASSERT_EQ(2, m_array[1].toInteger()); + ASSERT_EQ(3, m_array[2].toInteger()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +TEST_F(ArrayRead, all) +{ + try { + ASSERT_TRUE(m_arrayAll[0].isInteger()); + ASSERT_TRUE(m_arrayAll[0].isNumber()); + ASSERT_EQ(123, m_arrayAll[0].toInteger()); + + ASSERT_TRUE(m_arrayAll[1].isReal()); + ASSERT_TRUE(m_arrayAll[1].isNumber()); + ASSERT_EQ(9.2, m_arrayAll[1].toReal()); + + ASSERT_TRUE(m_arrayAll[2].isBoolean()); + ASSERT_TRUE(m_arrayAll[2].isFalse()); + ASSERT_FALSE(m_arrayAll[2].isTrue()); + + ASSERT_TRUE(m_arrayAll[3].isBoolean()); + ASSERT_TRUE(m_arrayAll[3].isTrue()); + ASSERT_FALSE(m_arrayAll[3].isFalse()); + + ASSERT_TRUE(m_arrayAll[4].isNull()); + + ASSERT_TRUE(m_arrayAll[5].isObject()); + ASSERT_TRUE(m_arrayAll[6].isArray()); + } catch (const JsonError &ex) { + FAIL() << ex.what(); + } +} + +/* -------------------------------------------------------- + * Object iterators + * -------------------------------------------------------- */ + +class ObjectIteratorsTest : public testing::Test { +protected: + JsonObject m_object; + +public: + ObjectIteratorsTest() + { + m_object.set("integer", 1); + m_object.set("string", "hello"); + m_object.set("boolean", true); + } +}; + +TEST_F(ObjectIteratorsTest, operators) +{ + // Read only (non const) + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, JsonValue> values; + JsonObject::iterator it = m_object.begin(); + JsonObject::iterator end = m_object.end(); + + while (it != end) { + values.insert({it->first, it->second}); + result.insert((*it++).first); + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInteger()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].isTrue()); + } + + // Read only (const) + { + std::set<std::string> expected{"boolean", "integer", "string"}; + std::set<std::string> result; + std::unordered_map<std::string, JsonValue> values; + JsonObject::const_iterator it = m_object.cbegin(); + JsonObject::const_iterator end = m_object.cend(); + + while (it != end) { + values.insert({it->first, it->second}); + result.insert((*it++).first); + } + + ASSERT_EQ(expected, result); + ASSERT_EQ(1, values["integer"].toInteger()); + ASSERT_EQ("hello", values["string"].toString()); + ASSERT_TRUE(values["boolean"].isTrue()); + } +} + +TEST_F(ObjectIteratorsTest, assign) +{ + // Assign (non const) + { + JsonObject::iterator it = m_object.begin(); + std::string key = it->first; + + it->second = JsonValue("CHANGED"); + + ASSERT_EQ("CHANGED", m_object[key].toString()); + ASSERT_EQ("CHANGED", it->second.toString()); + ASSERT_EQ(3, static_cast<int>(m_object.size())); + } +} + +TEST_F(ObjectIteratorsTest, assignConst) +{ + // Assign (const) + { + JsonObject::const_iterator it = m_object.cbegin(); + std::string key = it->first; + JsonValue orig = it->second; + + it->second = JsonValue("CHANGED"); + + ASSERT_TRUE(m_object.contains(key)); + ASSERT_EQ(orig, m_object[key]); + ASSERT_EQ(3, static_cast<int>(m_object.size())); + } +} + +/* -------------------------------------------------------- + * Array iterators + * -------------------------------------------------------- */ + +class ArrayIteratorsTest : public testing::Test { +protected: + JsonArray m_array; + +public: + ArrayIteratorsTest() + { + m_array.append(1); + m_array.append("hello"); + m_array.append(true); + } +}; + +TEST_F(ArrayIteratorsTest, operators) +{ + // Read only (non const) + { + JsonArray::iterator it = m_array.begin(); + + ASSERT_EQ(1, (*it).toInteger()); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ("hello", it[1].toString()); + ASSERT_TRUE(it[2].isTrue()); + + JsonArray::iterator it2 = it + 1; + ASSERT_EQ(1, it2[-1].toInteger()); + ASSERT_EQ("hello", it2->toString()); + ASSERT_TRUE(it2[1].isTrue()); + + JsonArray::iterator 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); + + ASSERT_TRUE(it2 > it); + ASSERT_FALSE(it3 > it); + + ASSERT_FALSE(it2 <= it); + ASSERT_TRUE(it3 <= it); + + ASSERT_FALSE(it2 < it); + ASSERT_FALSE(it3 < it); + } + + // Read only (const) + { + JsonArray::const_iterator it = m_array.cbegin(); + + ASSERT_EQ(1, (*it).toInteger()); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ("hello", it[1].toString()); + ASSERT_TRUE(it[2].isTrue()); + + JsonArray::const_iterator it2 = it + 1; + ASSERT_EQ(1, it2[-1].toInteger()); + ASSERT_EQ("hello", it2->toString()); + ASSERT_TRUE(it2[1].isTrue()); + + JsonArray::const_iterator 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); + + ASSERT_TRUE(it2 > it); + ASSERT_FALSE(it3 > it); + + ASSERT_FALSE(it2 <= it); + ASSERT_TRUE(it3 <= it); + + ASSERT_FALSE(it2 < it); + ASSERT_FALSE(it3 < it); + } +} + +TEST_F(ArrayIteratorsTest, assign) +{ + // Assign (non const) + { + JsonArray::iterator it = m_array.begin(); + + *it = JsonValue(9999); + + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(9999, it->toInteger()); + ASSERT_EQ(9999, m_array[0].toInteger()); + } +} + +TEST_F(ArrayIteratorsTest, assignConst) +{ + // Assign (const) + { + JsonArray::const_iterator it = m_array.cbegin(); + + *it = JsonValue(9999); + + ASSERT_EQ(3, static_cast<int>(m_array.size())); + ASSERT_EQ(1, it->toInteger()); + ASSERT_EQ(1, m_array[0].toInteger()); + } +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv);