Mercurial > code
diff C++/Json.h @ 311:ed3cc10761e4
Json:
* Split Json class into several classes
* Add unit tests
* Json is considered usable
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 13 Feb 2015 13:42:21 +0100 |
parents | 777bc3cb665a |
children | ea1a73a7d468 |
line wrap: on
line diff
--- a/C++/Json.h Wed Feb 11 19:52:13 2015 +0100 +++ b/C++/Json.h Fri Feb 13 13:42:21 2015 +0100 @@ -25,194 +25,159 @@ #include <string> #include <utility> #include <vector> -#include <unordered_map> #include <jansson.h> /** * @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 JsonObject and JsonArray respectively. + * + * However, copy constructors are implemented as deep copy so take care of + * not copying values mistakenly. */ /** - * @class Json - * @brief Json value - * - * This class contains one or more json values. + * @class JsonType + * @brief Json value type */ -class Json final { -public: - using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; - using Array = std::vector<Json>; - using Map = std::unordered_map<std::string, Json>; +enum class JsonType { + 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 +}; - /** - * @class Error - * @brief Json error - */ - class Error : public std::exception { - private: - friend class Json; - - std::string m_text; - std::string m_source; - int m_line; - int m_column; - unsigned m_position; +class JsonObject; +class JsonArray; - 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) - { - } - - public: - const char *what() const noexcept override - { - return m_text.c_str(); - } - }; +/** + * @class JsonValue + * @brief Encapsulate any JSON value + */ +class JsonValue { +public: + using Handle = std::unique_ptr<json_t, void (*)(json_t *)>; -private: - Handle m_handle; - Array m_list; - Map m_map; - - Json(Handle &&handle) - : m_handle(std::move(handle)) - { - } + friend class JsonObject; + friend class JsonArray; + friend class JsonDocument; - template <typename Func, typename... Args> - static Json from(Func func, Args&&... args) - { - json_error_t error; - json_t *value = func(std::forward<Args>(args)..., &error); - - if (!value) - throw Error(error); - - return Json(Handle(value, json_decref)); - } +protected: + /** + * The unique_ptr handle of json_t, will automatically decrease + * the reference count in its deleter. + */ + Handle m_handle; public: /** - * Load data from a string. + * Create a JsonValue from a native Jansson type. This function + * will increment the json_t reference count. * - * @param data the data - * @param flags the optional flags - * @return the json value - * @throw Error on failures + * @param json the value */ - static Json fromString(const std::string &data, int flags = 0); + JsonValue(json_t *json); /** - * Load data from a file. + * Deep copy of that element. * - * @param path the path - * @param flags the optional flags - * @return the json value - * @throw Error on failures + * @param value the other value */ - static Json fromFile(const std::string &path, int flags = 0); + JsonValue(const JsonValue &value); /** - * Create a json value of type JSON_OBJECT. + * Assign a deep copy of the other element. + * + * @return *this */ - explicit Json(); + JsonValue &operator=(const JsonValue &); + + /** + * Default move constructor. + * + * @param other the other value + */ + JsonValue(JsonValue &&other) = default; /** - * Create a JSON_NULL object. + * Default move assignment. * - * @param n the null value (nullptr) + * @param other the other value */ - explicit Json(std::nullptr_t n); + JsonValue &operator=(JsonValue &&) = default; /** - * Create a boolean object. - * - * @param value the boolean value + * Create an empty value (JsonType::Null). */ - explicit Json(bool value); + JsonValue(); /** - * Create a JSON_INTEGER object. + * Create a boolean value. * * @param value the value */ - explicit Json(int value); + JsonValue(bool value); /** - * Create a JSON_REAL object. + * Create a integer value (JsonType::Integer). * * @param value the value */ - explicit Json(double value); + JsonValue(int value); /** - * Create a JSON_STRING object. + * Create a real value (JsonType::Real). * * @param value the value */ - explicit Json(const std::string &value); + JsonValue(double value); + + /** + * Create a string value (JsonType::String). + * @param value + */ + JsonValue(std::string value); /** - * Create a JSON_ARRAY object. + * Create from a C string (JsonType::String). * - * @param list the list of children + * @param value the string */ - explicit Json(std::initializer_list<Json> list); + JsonValue(const char *value); /** - * Create a JSON_STRING object. + * Create from a string literal (JsonType::String). * - * @param str the string + * @param value the value */ - template <size_t N> - explicit Json(const char str[N]) - : m_handle(json_string(str), json_decref) + template <size_t Size> + inline JsonValue(char (&value)[Size]) + : m_handle(json_string(value), json_decref) { } /** - * Copy the object, this does a deep copy. - * - * @param json the other value - */ - Json(const Json &json); - - /** - * Copy the object, this does a deep copy. - * - * @param json the other value - * @return *this + * Default destructor. */ - Json &operator=(const Json &json); - - /** - * Move the object. - * - * @param json the other value - */ - Json(Json &&); - - /** - * Move the object. - * - * @param json the other value - * @return *this - */ - Json &operator=(Json &&); + virtual ~JsonValue() = default; /** * Get the type of value. * * @return the type */ - int typeOf() const; + JsonType typeOf() const; /** * Tells if the json value is an JSON_OBJECT. @@ -285,59 +250,6 @@ bool isBoolean() const; /** - * Get the number of values in the array - * - * @return the number or 0 - */ - unsigned size() const noexcept; - - /** - * Insert a copy of the value at the end. - * - * @param value the value to insert - */ - void append(const Json &value); - - /** - * Move the value at the end. - * - * @param value the value to insert - */ - void append(Json &&value); - - /** - * Insert a copy of the value at the specified index. - * - * @param value the value to insert - * @param index the position - */ - void insert(const Json &value, int index); - - /** - * Move the value at the specified index. - * - * @param value the value to insert - * @param index the position - */ - void insert(Json &&value, int index); - - /** - * Set a copy of value to the key 'name'. - * - * @param value the value - * @param name the key name. - */ - void set(const Json &value, const std::string &name); - - /** - * Move value to the key 'name'. - * - * @param value the value - * @param name the key name. - */ - void set(Json &&value, const std::string &name); - - /** * Get the string value. * * @return the string @@ -359,68 +271,367 @@ double toReal() const noexcept; /** - * Dump the value as a string. + * Convert to object. + * + * @return an object + */ + JsonObject toObject() const; + + /** + * Convert to array. * - * @param flags the optional flags - * @return the string + * @return an array */ - std::string dump(int flags = 0); + 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(); + } /** - * Dump the value to a path. + * Overloaded function. * - * @param path the path - * @param flags the optional flags + * @return the json_t handle */ - void dump(const std::string &path, int flags = 0); + 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 value at the specified index. + * 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 - * @return the reference to the value - * @throw std::invalid_argument on error - * @throw std::out_of_range on bad arguments */ - Json &operator[](int index); + void insert(const JsonValue &value, int index); /** * Get the value at the specified index. * * @param index the position - * @return the reference to the value + * @return the value * @throw std::invalid_argument on error * @throw std::out_of_range on bad arguments */ - const Json &operator[](int index) const; + JsonValue operator[](int index) const; /** - * Get the value at the specified key. + * Iterate over the array value (not recursively). + * + * The function must have the following signature: + * void f(int, const JsonValue &) * - * @param name the key - * @return the reference to the value + * @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 */ - Json &operator[](const std::string &name); + 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{}; + int m_column{}; + unsigned m_position{}; + +public: + /** + * Custom error with no line, no column and no position. + * + * @param error the error message + */ + inline JsonError(std::string error) + : m_text(std::move(error)) + { + } + + /** + * Error from a json_error_t. + * + * @param error the error + */ + inline JsonError(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) + { + } + + /** + * Get the error message. + * + * @return the message + */ + const char *what() const noexcept override + { + return m_text.c_str(); + } + + /** + * Get the text message. + * + * @return the text + */ + inline const std::string &text() const noexcept + { + return m_text; + } + + /** + * Get the source. + * + * @return the source + */ + inline const std::string &source() const noexcept + { + return m_source; + } + + /** + * Get the line. + * + * @return the line + */ + inline int line() const noexcept + { + return m_line; + } /** - * Get the value at the specified key. + * Get the column. + * + * @return the column + */ + inline int column() const noexcept + { + return m_column; + } + + /** + * Get the position. * - * @param name the key - * @return the reference to the value - * @throw std::invalid_argument on error - * @throw std::out_of_range on bad arguments + * @return the position + */ + inline int position() const noexcept + { + return m_position; + } +}; + +/** + * @class JsonReader + * @brief Base class for JSON reading + */ +class JsonReader { +public: + /** + * Default constructor. */ - const Json &operator[](const std::string &name) const; + JsonReader() = default; + + /** + * Default destructor. + */ + virtual ~JsonReader() = default; + + /** + * Read the source and return a value. + * + * @return a value + */ + virtual JsonValue read() = 0; +}; + +/** + * @class JsonWriter + * @brief Base class for writing + */ +class JsonWriter { +public: + /** + * Default destructor. + */ + JsonWriter() = default; /** - * Compare the values. + * Default destructor. + */ + virtual ~JsonWriter() = default; + + /** + * Write to the source the value. + * + * @param value the value + */ + virtual void write(const JsonValue &value) = 0; +}; + +/** + * @class JsonReaderFile + * @brief Read JSON data from a file + */ +class JsonReaderFile final : public JsonReader { +private: + std::string m_path; + +public: + /** + * Construct a JsonReader to a file. This function does not open + * the file immediately. * - * @param j1 the first value - * @param j2 the second value + * @param path the path + */ + JsonReaderFile(std::string path); + + /** + * Read the file and extract a JsonValue. + * + * @return a JsonValue (which will be JsonType::Object or JsonType::Array) + * @throw JsonError on errors */ - friend bool operator==(const Json &j1, const Json &j2); + JsonValue read() override; +}; + +/** + * @class JsonWriterFile + * @brief Write JSON data to file + */ +class JsonWriterFile final : public JsonWriter { +private: + std::string m_path; + +public: + /** + * Construct a JsonReader to a file. This function does not open + * the file immediately. + * + * @param path + */ + JsonWriterFile(std::string path); + + /** + * Write to the file. + * + * @param value the value + * @throw JsonError on errors + */ + void write(const JsonValue &value) override; }; #endif // !_JSON_H_