# HG changeset patch # User David Demelier # Date 1444057480 -7200 # Node ID f8147806590183f7ff3ad2918e9a2eb7dd9b92d9 # Parent 9ab878fb9fa289e33c433dd349811b4d69f79249 Json: - Complete rewrite, - Convert jansson tree to own C++ tree, - Conversion from Value to Object/Array makes copies diff -r 9ab878fb9fa2 -r f81478065901 C++/modules/Json/Json.cpp --- a/C++/modules/Json/Json.cpp Mon Oct 05 14:39:57 2015 +0200 +++ b/C++/modules/Json/Json.cpp Mon Oct 05 17:04:40 2015 +0200 @@ -1,5 +1,5 @@ /* - * Json.cpp -- jansson C++11 wrapper + * Json.cpp -- C++14 JSON manipulation using jansson parser * * Copyright (c) 2013-2015 David Demelier * @@ -16,147 +16,147 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include "Json.h" -#include "Json.h" +#include namespace json { -/* -------------------------------------------------------- - * Object - * -------------------------------------------------------- */ +namespace { + +void readObject(Value &parent, json_t *object); +void readArray(Value &parent, json_t *array); + +Value readValue(json_t *v) +{ + if (json_is_null(v)) { + return Value{nullptr}; + } + if (json_is_string(v)) { + return Value{json_string_value(v)}; + } + if (json_is_number(v)) { + return Value{json_number_value(v)}; + } + if (json_is_boolean(v)) { + return Value{json_boolean_value(v)}; + } + if (json_is_object(v)) { + Object object; + + readObject(object, v); + + return object; + } + if (json_is_array(v)) { + Array array; + + readArray(array, v); + + return array; + } + + return Value{}; +} + +void readObject(Value &parent, json_t *object) +{ + const char *key; + json_t *value; + + json_object_foreach(object, key, value) { + static_cast(parent).insert(key, readValue(value)); + } +} + +void readArray(Value &parent, json_t *array) +{ + size_t index; + json_t *value; + + json_array_foreach(array, index, value) { + static_cast(parent).append(readValue(value)); + } +} + +template +Value convert(Func fn, Args&&... args) +{ + json_error_t error; + json_t *json = fn(std::forward(args)..., &error); + + if (json == nullptr) { + throw Error{error.text, error.source, error.line, error.column, error.position}; + } + + Value value; + + if (json_is_object(json)) { + value = Object{}; + readObject(value, json); + } else { + value = Array{}; + readArray(value, json); + } + + json_decref(json); + + return value; +} + +} // !namespace + +bool Value::toBool() const noexcept +{ + if (m_type != Type::Boolean) + return false; + + return m_boolean; +} + +double Value::toNumber() const noexcept +{ + if (m_type != Type::Number) + return 0; + + return m_number; +} + +std::string Value::toString() const noexcept +{ + if (m_type != Type::String) { + return ""; + } + + return m_string; +} Object Value::toObject() const noexcept { - json_incref(m_handle.get()); + if (m_type != Type::Object) { + return Object{}; + } - return Object(m_handle.get()); + return Object(*this); } Array Value::toArray() const noexcept { - json_incref(m_handle.get()); - - return Array(m_handle.get()); -} - -/* -------------------------------------------------------- - * Array - * -------------------------------------------------------- */ - -Value Array::at(int index) const -{ - auto value = json_array_get(m_handle.get(), index); - - if (value == nullptr) - throw Error("index out of bounds"); - - json_incref(value); - - return Value{value}; -} - -Value Array::operator[](int index) const noexcept -{ - auto value = json_array_get(m_handle.get(), index); - - if (value == nullptr) - return Value(); - - json_incref(value); + if (m_type != Type::Array) { + return Array{}; + } - return Value(value); -} - -Array::Ref Array::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); -} - -/* -------------------------------------------------------- - * Object - * -------------------------------------------------------- */ - -Object::Ref Object::operator[](const std::string &name) -{ - if (typeOf() != Type::Object) - return Ref(Value(), *this, name); - - auto value = json_object_get(m_handle.get(), name.c_str()); - - json_incref(value); - - return Ref(value, *this, name); + return Array(*this); } -Value Object::operator[](const std::string &name) const +Document::Document(Buffer buffer) { - if (typeOf() != Type::Object) - return Value(); - - auto value = json_object_get(m_handle.get(), name.c_str()); - - if (value == nullptr) - return Value(); - - json_incref(value); - - return Value(value); -} - -/* -------------------------------------------------------- - * Document - * -------------------------------------------------------- */ - -Value Document::read(std::string content, int flags) const -{ - json_error_t error; - json_t *json = json_loads(content.c_str(), flags, &error); - - if (json == nullptr) - throw Error(error); - - return Value(json); + m_value = convert(json_loads, buffer.text.c_str(), 0); } -Value Document::read(std::ifstream &stream, int flags) const +Document::Document(File file) { - if (!stream.is_open()) - throw Error("File not opened"); - - 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); -} - -Document::Document(std::ifstream &stream, int flags) -{ - m_value = read(stream, flags); -} - -Document::Document(std::ifstream &&stream, int flags) -{ - m_value = read(stream, flags); -} - -Document::Document(std::string content, int flags) -{ - m_value = read(std::move(content), flags); + m_value = convert(json_load_file, file.path.c_str(), 0); } } // !json \ No newline at end of file diff -r 9ab878fb9fa2 -r f81478065901 C++/modules/Json/Json.h --- a/C++/modules/Json/Json.h Mon Oct 05 14:39:57 2015 +0200 +++ b/C++/modules/Json/Json.h Mon Oct 05 17:04:40 2015 +0200 @@ -1,5 +1,5 @@ /* - * Json.h -- jansson C++11 wrapper + * Json.h -- C++14 JSON manipulation using jansson parser * * Copyright (c) 2013-2015 David Demelier * @@ -19,1589 +19,734 @@ #ifndef _JSON_H_ #define _JSON_H_ -#include -#include -#include -#include -#include -#include -#include -#include +/** + * @file Json.h + * @brief Jansson C++14 wrapper + * + * These classes can be used to build or parse JSON documents using jansson library. It is designed to be safe + * and explicit. It does not implement implicit sharing like jansson so when you access (e.g. Value::toObject) values + * you get real copies, thus when you read big documents it can has a performance cost. + */ + +#include #include #include - -#include - -/** - * @file Json.h - * @brief A jansson C++ modern wrapper - * - * Because of the Jansson implementation, all these classes are implicitly - * shared. - * - * This means that you can't set any value to an existing value as it would - * change a value which may be used somewhere else, instead you must set - * or replace elements in Object and Array respectively. - * - * However, copy constructors are implemented as deep copy so take care of - * not copying values mistakenly. - */ +#include namespace json { /** - * @class Type - * @brief Json value type + * @enum Type + * @brief Type of Value. */ enum class Type { - Object = JSON_OBJECT, //!< Object - Array = JSON_ARRAY, //!< Array - String = JSON_STRING, //!< String - Integer = JSON_INTEGER, //!< Integer - Real = JSON_REAL, //!< Floating point - True = JSON_TRUE, //!< Boolean true - False = JSON_FALSE, //!< Boolean false - Null = JSON_NULL //!< Empty or null + Array, //!< Value is an array [] + Boolean, //!< Value is boolean + Number, //!< Value is number + Object, //!< Value is object {} + String, //!< Value is unicode string + Null, //!< Value is defined to null + Undefined //!< Value is undefined }; /** * @class Error - * @brief Error thrown for any error + * @brief Error description. */ -class Error final : public std::exception { -private: - std::string m_text; - std::string m_source; - int m_line{}; - int m_column{}; - int m_position{}; +class Error { +public: + std::string text; //!< The error message + std::string source; //!< The source (e.g. name of file) + int line{0}; //!< The line number + int column{0}; //!< The column + int position{0}; //!< The position +}; + +class Array; +class Object; + +/** + * @class Value + * @brief Generic JSON value wrapper. + */ +class Value { +protected: + Type m_type; + + union { + double m_number; + bool m_boolean; + std::string m_string; + std::vector m_array; + std::map m_map; + }; public: /** - * Custom error with no line, no column and no position. - * - * @param error the error message + * Construct an undefined value. */ - inline Error(std::string error) - : m_text(std::move(error)) + inline Value() noexcept + : m_type{Type::Undefined} { } /** - * Error from a json_error_t. - * - * @param error the error + * Construct a null value. */ - inline Error(const json_error_t &error) - : m_text(error.text) - , m_source(error.source) - , m_line(error.line) - , m_column(error.column) - , m_position(error.position) + inline Value(std::nullptr_t) noexcept + : m_type{Type::Null} + { + } + + /** + * Construct a boolean value. + * + * @param value the boolean value + */ + inline Value(bool value) noexcept + : m_type{Type::Boolean} + , m_boolean{value} { } /** - * Get the error message. + * Construct a value from a C-string. * - * @return the message + * @param value the C-string */ - const char *what() const noexcept override + inline Value(const char *value) + : m_type{Type::String} { - return m_text.c_str(); - } - - /** - * Get the text message. - * - * @return the text - */ - inline const std::string &text() const noexcept - { - return m_text; + new (&m_string) std::string{value}; } /** - * Get the source. + * Construct a number value. * - * @return the source + * @param value the real value */ - inline const std::string &source() const noexcept + inline Value(double value) noexcept + : m_type{Type::Number} + , m_number{value} { - return m_source; } /** - * Get the line. + * Construct a string value. * - * @return the line + * @param value the string */ - inline int line() const noexcept + inline Value(std::string value) noexcept + : m_type{Type::String} { - return m_line; - } - - /** - * Get the column. - * - * @return the column - */ - inline int column() const noexcept - { - return m_column; + new (&m_string) std::string{std::move(value)}; } /** - * Get the position. + * Move constructor. * - * @return the position + * @param other the value to move from */ - inline int position() const noexcept + inline Value(Value &&other) { - return m_position; - } -}; - -class Object; -class Array; - -/** - * @class Value - * @brief Encapsulate any JSON value - */ -class Value { -public: - using Handle = std::unique_ptr; - - friend class Object; - friend class Array; + m_type = other.m_type; -protected: - /** - * The unique_ptr handle of json_t, will automatically decrease - * the reference count in its deleter. - */ - Handle m_handle; - - inline void check() const - { - if (m_handle == nullptr) - throw Error(std::strerror(errno)); - } - -public: - /** - * Deep copy of that element. - * - * @param value the other value - * @throw Error on allocation error - */ - inline Value(const Value &value) - : m_handle(json_deep_copy(value.m_handle.get()), json_decref) - { - check(); + switch (m_type) { + case Type::String: + new (&m_string) std::string(); + m_string = std::move(other.m_string); + break; + case Type::Number: + m_number = other.m_number; + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Object: + new (&m_map) std::map(); + m_map = std::move(other.m_map); + break; + case Type::Array: + new (&m_array) std::vector(); + m_array = std::move(other.m_array); + break; + default: + break; + } } /** - * Assign a deep copy of the other element. + * Copy constructor. * - * @return *this - * @throw Error on allocation error + * @param other the value to copy from */ - inline Value &operator=(const Value &value) + inline Value(const Value &other) { - m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref); + m_type = other.m_type; - check(); - - return *this; + switch (m_type) { + case Type::String: + new (&m_string) std::string(); + m_string = other.m_string; + break; + case Type::Number: + m_number = other.m_number; + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Object: + new (&m_map) std::map(); + m_map = other.m_map; + break; + case Type::Array: + new (&m_array) std::vector(); + m_array = other.m_array; + break; + default: + break; + } } /** - * Move constructor, the other value is left empty (Type::Null). + * Copy operator. * - * @param other the other value + * @param other the value to copy from + * @return *this */ - inline Value(Value &&other) noexcept - : m_handle(std::move(other.m_handle)) + inline Value &operator=(const Value &other) { - other.m_handle = Handle(json_null(), json_decref); - } + m_type = other.m_type; - /** - * Move assignment, the other value is left empty (Type::Null). - * - * @param other the other value - */ - inline Value &operator=(Value &&other) noexcept - { - m_handle = std::move(other.m_handle); - other.m_handle = Handle(json_null(), json_decref); + switch (m_type) { + case Type::String: + new (&m_string) std::string(); + m_string = other.m_string; + break; + case Type::Number: + m_number = other.m_number; + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Object: + new (&m_map) std::map(); + m_map = other.m_map; + break; + case Type::Array: + new (&m_array) std::vector(); + m_array = other.m_array; + break; + default: + break; + } return *this; } /** - * Create a Value from a native Jansson type. This function - * will increment the json_t reference count. + * Move operator. * - * @param json the value - */ - inline Value(json_t *json) noexcept - : m_handle(json, json_decref) - { - } - - /** - * Construct a null value from a nullptr argument. - */ - inline Value(std::nullptr_t) noexcept - : m_handle(json_null(), json_decref) - { - } - - /** - * Create an empty value (Type::Null). + * @param other the value to move from */ - inline Value() noexcept - : m_handle(json_null(), json_decref) - { - } - - /** - * Create a boolean value. - * - * @param value the value - */ - inline Value(bool value) noexcept - : m_handle(json_boolean(value), json_decref) + inline Value &operator=(Value &&other) { - } + m_type = other.m_type; - /** - * Create a integer value (Type::Integer). - * - * @param value the value - * @throw Error on allocation error - */ - inline Value(int value) - : m_handle(json_integer(value), json_decref) - { - check(); + switch (m_type) { + case Type::String: + new (&m_string) std::string(); + m_string = std::move(other.m_string); + break; + case Type::Number: + m_number = other.m_number; + break; + case Type::Boolean: + m_boolean = other.m_boolean; + break; + case Type::Object: + new (&m_map) std::map(); + m_map = std::move(other.m_map); + break; + case Type::Array: + new (&m_array) std::vector(); + m_array = std::move(other.m_array); + break; + default: + break; + } + + return *this; } /** - * Create a real value (Type::Real). - * - * @param value the value - * @throw Error on allocation error + * Destructor. */ - inline Value(double value) - : m_handle(json_real(value), json_decref) + inline ~Value() { - check(); - } - - /** - * Create a string value (Type::String). - * - * @param value the value - * @throw Error on allocation error - */ - inline Value(std::string value) - : m_handle(json_string(value.c_str()), json_decref) - { - check(); + switch (m_type) { + case Type::String: + m_string.~basic_string(); + break; + case Type::Object: + m_map.~map(); + break; + case Type::Array: + m_array.~vector(); + break; + default: + break; + } } /** - * Create from a C string (Type::String). - * - * @param value the string - * @throw Error on allocation error - */ - inline Value(const char *value) - : m_handle(json_string(value), json_decref) - { - check(); - } - - /** - * Create from a string literal (Type::String). - * - * @param value the value - * @throw Error on allocation error - */ - template - inline Value(char (&value)[Size]) - : m_handle(json_string(value), json_decref) - { - check(); - } - - /** - * Default destructor. - */ - virtual ~Value() = default; - - /** - * Get the type of value. + * Get the value type. * * @return the type */ inline Type typeOf() const noexcept { - return static_cast(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()); + return m_type; } /** - * Tells if the json value is an JSON_NULL. + * Get the value as boolean. * - * @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 + * @return the value or false if not a boolean */ - 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()); - } + bool toBool() const noexcept; /** - * Tells if the json value is an JSON_TRUE or JSON_FALSE. + * Get the value as real. * - * @return true or false + * @return the value or 0 if not a real */ - inline bool isBoolean() const noexcept - { - return json_is_boolean(m_handle.get()); - } - - /** - * 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; - } + double toNumber() const noexcept; /** - * Get the integer value. + * Get the value as string. * - * @return the value or 0 + * @return the value or empty stirng if not a string */ - inline int toInteger() const noexcept - { - return json_integer_value(m_handle.get()); - } + std::string toString() const noexcept; /** - * Get the real value. + * Convert the value to object. * - * @return the value or 0 - */ - inline double toReal() const noexcept - { - return json_real_value(m_handle.get()); - } - - /** - * Convert to object. - * - * @return an 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 to array. + * Convert the value to array. * - * @return an 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; +}; +/** + * @class Array + * @brief Array definition. + */ +class Array : public Value { +public: /** - * Write to a stream. - * - * @param out the out - * @param flags the optional Jansson flags + * Construct an empty array. */ - inline void write(std::ofstream &out, int flags = 0) const + Array() { - auto content = dump(flags); - - std::copy(std::begin(content), std::end(content), std::ostreambuf_iterator(out)); + m_type = Type::Array; + new (&m_array) std::vector(); } /** - * Overloaded function. + * Copy constructor from value for safe casts. + * + * @param v the value to copy from + */ + Array(const Value &v) + : Value{v} + { + } + + /** + * Move constructor from value for safe casts. * - * @param out the out - * @param flags the optional Jansson flags + * @param v the value to move from + */ + Array(Value &&v) + : Value{std::move(v)} + { + } + + /** + * Get non-const iterator to the beginning. + * + * @return the iterator */ - inline void write(std::ofstream &&out, int flags = 0) const + inline auto begin() noexcept { - write(out, flags); + return m_array.begin(); + } + + /** + * Get a const iterator to the beginning. + * + * @return the iterator + */ + inline auto begin() const noexcept + { + return m_array.begin(); } /** - * Convert the Json value as a string. + * Get a const iterator to the beginning. * - * @return the string - * @param flags the optional Jansson flags + * @return the iterator */ - inline std::string dump(int flags = 0) const + inline auto cbegin() const noexcept { - auto str = json_dumps(m_handle.get(), flags); - - if (str == nullptr) - return ""; + return m_array.cbegin(); + } - std::string ret(str); - std::free(str); + /** + * Get a non-const iterator to the end. + * + * @return the iterator + */ + inline auto end() noexcept + { + return m_array.end(); + } - return ret; + /** + * Get a const iterator to the end. + * + * @return the iterator + */ + inline auto end() const noexcept + { + return m_array.end(); } /** - * Convert to native Jansson type. + * Get a const iterator to the end. * - * You should not call json_incref or json_decref on it as it is - * automatically done. + * @return the iterator + */ + inline auto cend() const noexcept + { + return m_array.cend(); + } + + /** + * Get a value at the specified index. * - * @return the json_t handle - * @warning use this function with care + * @param position the position + * @return the value + * @throw std::out_of_range if out of bounds */ - inline operator json_t *() noexcept + inline const Value &at(unsigned position) const { - return m_handle.get(); + return m_array.at(position); + } + + /** + * Get a value at the specified index. + * + * @param position the position + * @return the value + * @throw std::out_of_range if out of bounds + */ + inline Value &at(unsigned position) + { + return m_array.at(position); + } + + /** + * Get a value at the specified index. + * + * @param position the position + * @return the value + */ + inline const Value &operator[](unsigned position) const + { + return m_array[position]; } /** * Overloaded function. * - * @return the json_t handle + * @param position the position + * @return the value */ - inline operator const json_t *() const noexcept + inline Value &operator[](unsigned position) { - return m_handle.get(); + return m_array[position]; } /** - * Equality operator. + * Add a new value. + * + * @param value the value to append */ - inline bool operator==(const Value &other) const noexcept + template + inline void append(T &&value) { - return json_equal(m_handle.get(), other.m_handle.get()); + m_array.push_back(std::forward(value)); } }; /** - * @class Array - * @brief Manipulate JSON arrays + * @class Object + * @brief Object definition. */ -class Array final : public Value { +class Object : public Value { public: /** - * @class Ref - * @brief Reference wrapper to be assignable + * Construct an empty object. */ - class Ref final : public Value { - private: - Array &m_array; - int m_index; - - public: - explicit inline Ref(Value value, Array &array, int index) - : Value(std::move(value)) - , m_array(array) - , m_index(index) - { - } - - inline operator Value() const noexcept - { - return *this; - } - - inline Value &operator*() noexcept - { - return *this; - } - - inline Value *operator->() noexcept - { - return this; - } - - inline Ref &operator=(const Value &value) - { - m_array.replace(value, m_index); - - return *this; - } - - inline Ref &operator=(Value &&value) - { - m_array.replace(std::move(value), m_index); - - return *this; - } - }; + Object() + { + m_type = Type::Object; + new (&m_map) std::map(); + } /** - * @class Ptr - * @brief Pointer wrapper for Value iterators + * Copy constructor from value for safe casts. + * + * @param v the value to copy from */ - class Ptr final : public Value { - public: - explicit Ptr(Value value) noexcept - : Value(std::move(value)) - { - } - - inline Value &operator*() noexcept - { - return *this; - } - - inline Value *operator->() noexcept - { - return this; - } - }; - - class iterator final { - public: - using iterator_category = std::random_access_iterator_tag; - using difference_type = int; - using value_type = Value; - using reference = Ref; - using pointer = Ptr; - - friend class Array; - - private: - Array &m_array; - int m_index; - - public: - explicit inline iterator(Array &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; - } - }; + Object(const Value &v) + : Value{v} + { + } - class const_iterator final { - public: - using iterator_category = std::random_access_iterator_tag; - using difference_type = int; - using value_type = Value; - using reference = Value; - using pointer = Ptr; - - friend class Array; - - private: - const Array &m_array; - int m_index; - - public: - explicit inline const_iterator(const Array &array, int index = 0) noexcept - : m_array(array) - , m_index(index) - { - } - - inline Value operator*() const - { - return m_array.at(m_index); - } - - inline Ptr operator->() const - { - return Ptr(m_array.at(m_index)); - } - - inline Value 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; - } + /** + * Move constructor from value for safe casts. + * + * @param v the value to move from + */ + Object(Value &&v) + : Value{std::move(v)} + { + } - 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 = Value; - using const_pointer = const value_type *; - using reference = Value &; - using const_reference = const Value &; - using difference_type = int; - -protected: - using Value::Value; - -public: /** - * Create an empty array. + * Get non-const iterator to the beginning. * - * @throw Error on allocation error + * @return the iterator */ - inline Array() - : Value(json_array()) + inline auto begin() noexcept { - check(); + return m_map.begin(); } /** - * Create an array from a list of values. - * - * @param list the list - * @throw Error on allocation error - */ - inline Array(std::initializer_list list) - : Array() - { - for (auto &v : list) - append(std::move(v)); - } - - /** - * Returns an iterator to the beginning. + * Get a const iterator to the beginning. * * @return the iterator */ - inline iterator begin() noexcept + inline auto begin() const noexcept { - return iterator(*this, 0); + return m_map.begin(); } /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator begin() const noexcept - { - return const_iterator(*this, 0); - } - - /** - * Overloaded function. + * Get a const iterator to the beginning. * * @return the iterator */ - inline const_iterator cbegin() const noexcept + inline auto cbegin() const noexcept { - return const_iterator(*this, 0); + return m_map.cbegin(); } /** - * Returns an iterator to the end. + * Get a non-const iterator to the end. * * @return the iterator */ - inline iterator end() noexcept + inline auto end() noexcept { - return iterator(*this, size()); + return m_map.end(); } /** - * Overloaded function. + * Get a const iterator to the end. * * @return the iterator */ - inline const_iterator end() const noexcept + inline auto end() const noexcept { - return const_iterator(*this, size()); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline const_iterator cend() const noexcept - { - return const_iterator(*this, size()); + return m_map.end(); } /** - * Get a value. + * Get a const iterator to the end. * - * @param index the index - * @throw Error on error + * @return the iterator */ - Value at(int index) const; - - /** - * Erase the array content. - */ - inline void clear() noexcept + inline auto cend() const noexcept { - json_array_clear(m_handle.get()); + return m_map.cend(); } /** - * Remove the element at the specified index. + * Get a value from the object. * - * @param index the index + * @param name the value key + * @return the value + * @throw std::out_of_range if not found */ - inline void erase(int index) noexcept + inline const Value &at(const std::string &name) const { - json_array_remove(m_handle.get(), index); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(iterator it) noexcept - { - erase(it.m_index); + return m_map.at(name); } /** * Overloaded function. * - * @param it the iterator - */ - inline void erase(const_iterator it) noexcept - { - erase(it.m_index); - } - - /** - * Get the number of values in the array - * - * @return the number or 0 + * @param name the value key + * @return the value + * @throw std::out_of_range if not found */ - 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 Value &value) noexcept + inline Value &at(const std::string &name) { - json_array_insert(m_handle.get(), 0, value.m_handle.get()); - } - - /** - * Insert a copy of the value at the end. - * - * @param value the value to insert - */ - inline void append(const Value &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 - */ - inline void insert(const Value &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 Value &value, int index) - { - json_array_set(m_handle.get(), index, value.m_handle.get()); + return m_map.at(name); } /** - * Get the value at the specified index. + * Get a value from the object. * - * @param index the position + * @param name the value key * @return the value */ - Value 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 Object - * @brief Object wrapper - */ -class Object final : public Value { -public: - using key_type = std::string; - using mapped_type = Value; - using size_type = int; - using value_type = std::pair; - using const_pointer = const value_type *; - using reference = Value &; - using const_reference = const Value &; - using difference_type = int; - - /** - * @class Ref - * @brief Wrapper for updating Object - * - * This class is only used for the following functions: - * - * Object::operator[] - */ - class Ref final : public Value { - private: - Object &m_object; - std::string m_key; - - public: - explicit inline Ref(Value value, Object &object, std::string key) - : Value(std::move(value)) - , m_object(object) - , m_key(std::move(key)) - { - } - - inline operator Value() const noexcept - { - return *this; - } - - inline Value &operator*() noexcept - { - return *this; - } - - inline Value *operator->() noexcept - { - return this; - } - - inline Ref &operator=(const Value &value) - { - m_object.set(m_key, value); - - return *this; - } - - inline Ref &operator=(Value &&value) - { - m_object.set(m_key, std::move(value)); - - return *this; - } - }; - - /** - * @class Ptr - * @brief Pointer wrapper for Value iterators - * - * For const iterators, the real type is a Value, for non const - * iterators it's a ref so that user can edit it. - */ - template - class Ptr final { - private: - std::pair m_pair; - - public: - inline Ptr(std::pair value) noexcept - : m_pair(std::move(value)) - { - } - - inline std::pair &operator*() noexcept - { - return m_pair; - } - - inline std::pair *operator->() noexcept - { - return &m_pair; - } - }; - - /** - * @class iterator - * @brief Forward iterator - */ - class iterator { - public: - using value_type = std::pair; - - friend class Object; - - private: - Object &m_object; - void *m_keyIt; - - inline std::string key() const noexcept - { - return json_object_iter_key(m_keyIt); - } - - public: - explicit inline iterator(Object &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 operator->() const - { - auto k = key(); - - return Ptr({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 bool operator==(iterator other) const noexcept - { - return m_keyIt == other.m_keyIt; - } - - inline bool 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; - - friend class Object; - - private: - const Object &m_object; - void *m_keyIt; - - inline std::string key() const noexcept - { - return json_object_iter_key(m_keyIt); - } - - public: - explicit inline const_iterator(const Object &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 operator->() const - { - auto k = key(); - - return Ptr({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 bool operator==(const_iterator other) const noexcept - { - return m_keyIt == other.m_keyIt; - } - - inline bool operator!=(const_iterator other) const noexcept - { - return m_keyIt != other.m_keyIt; - } - }; - -protected: - using Value::Value; - -public: - /** - * Create empty object. - * - * @throw Error on allocation error - */ - inline Object() - : Value(json_object()) + inline const Value &operator[](const std::string &name) const { - check(); - } - - /** - * Create a Object from an initializer_list. - * - * @param list the list of key-value pairs - * @throw Error on allocation error - */ - inline Object(std::initializer_list list) - : Object() - { - 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())); + return m_map[name]; } /** * Overloaded function. * - * @return the iterator - */ - inline const_iterator begin() const noexcept - { - return const_iterator(*this, json_object_iter(m_handle.get())); - } - - /** - * Overloaded function. - * - * @return the iterator + * @param name the value key + * @return the value */ - 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 + inline Value &operator[](const std::string &name) { - return iterator(*this, nullptr); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - 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; + return m_map[name]; } /** - * 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()); - } - - /** - * Remove all elements from the object. - */ - inline void clear() noexcept - { - json_object_clear(m_handle.get()); - } - - /** - * Remove element `key' if exists. + * Insert a new value. * - * @param key the key - */ - inline void erase(const std::string &key) noexcept - { - json_object_del(m_handle.get(), key.c_str()); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(iterator it) noexcept - { - erase(it.key()); - } - - /** - * Overloaded function. - * - * @param it the iterator - */ - inline void erase(const_iterator it) noexcept - { - erase(it.key()); - } - - /** - * Set the value as key in the object. - * - * @param key the key + * @param name the key * @param value the value */ - inline void set(const std::string &key, const Value &value) noexcept + inline void insert(std::string name, Value value) { - json_object_set(m_handle.get(), key.c_str(), value.m_handle.get()); + m_map.insert({std::move(name), std::move(value)}); } +}; - /** - * 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); +/** + * @class Buffer + * @brief Open JSON document from text. + */ +class Buffer { +public: + std::string text; //!< The JSON text +}; - /** - * Get the value at the specified key. If the value is not found, an - * empty value is returned - * - * @param key the key - * @return the value - */ - Value operator[](const std::string &key) const; +/** + * @class File + * @brief Open JSON document from a file. + */ +class File { +public: + std::string path; //!< The path to the file }; /** * @class Document - * @brief Read files and strings to create Json values + * @brief Construct a JSON document from a source + * @see Buffer + * @see File */ -class Document final { +class Document { private: Value m_value; - Value read(std::string, int flags) const; - Value read(std::ifstream &stream, int flags) const; - public: /** - * Construct a document from a file. + * Construct a document from a buffer. * - * @param stream the stream - * @param flags the optional Jansson flags + * @param buffer the text * @throw Error on errors */ - Document(std::ifstream &fstream, int flags = 0); + Document(Buffer buffer); /** * Construct a document from a file. * - * @param stream the stream - * @param flags the optional Jansson flags + * @param file the file * @throw Error on errors */ - Document(std::ifstream &&stream, int flags = 0); + Document(File file); /** - * Construct a document from a file. - * - * @param stream the stream - * @param flags the optional Jansson flags - * @throw Error on errors - */ - Document(std::string content, int flags = 0); - - /** - * Check if the document contains an object. + * Check if the opened document is an object. * * @return true if object */ inline bool isObject() const noexcept { - return m_value.isObject(); + return m_value.typeOf() == Type::Object; } /** - * Check if the document contains an array. + * Check if the opened document is an array. * * @return true if array */ inline bool isArray() const noexcept { - return m_value.isArray(); + return m_value.typeOf() == Type::Array; } /** - * Convert the document value to object + * Get the object. * - * @return the value as object + * @return the object or empty object if not an object */ inline Object toObject() const noexcept { - return m_value.toObject(); + if (m_value.typeOf() != Type::Object) { + return Object{}; + } + + return Object(m_value); } /** - * Convert the document value to array + * Get the array. * - * @return the value as array + * @return the array or empty object if not an array */ inline Array toArray() const noexcept { - return m_value.toArray(); + if (m_value.typeOf() != Type::Array) { + return Array{}; + } + + return Array(m_value); } }; } // !json -#endif // !_JSON_H_ +#endif // !_JSON_H_ \ No newline at end of file