Mercurial > code
view C++/Json.h @ 321:0eb59106f700
Json: move operators now makes the other value JsonType::Null to be still usable.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 27 Feb 2015 08:48:40 +0100 |
parents | b67b2da45bb0 |
children | 0e80e4589533 |
line wrap: on
line source
/* * Json.h -- jansson C++11 wrapper * * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _JSON_H_ #define _JSON_H_ #include <algorithm> #include <cstdlib> #include <initializer_list> #include <fstream> #include <iterator> #include <memory> #include <string> #include <utility> #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 JsonType * @brief Json value type */ 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 JsonError * @brief Error thrown for any error */ class JsonError final : public std::exception { private: std::string m_text; std::string m_source; int m_line{}; int m_column{}; int 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 column. * * @return the column */ inline int column() const noexcept { return m_column; } /** * Get the position. * * @return the position */ inline int position() const noexcept { return m_position; } }; 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; protected: /** * The unique_ptr handle of json_t, will automatically decrease * the reference count in its deleter. */ Handle m_handle; public: /** * 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 */ inline JsonValue &operator=(const JsonValue &value) { m_handle = Handle(json_deep_copy(value.m_handle.get()), json_decref); return *this; } /** * Move constructor, the other value is left empty (JsonType::Null). * * @param other the other value */ inline JsonValue(JsonValue &&other) : m_handle(std::move(other.m_handle)) { other.m_handle = Handle(json_null(), json_decref); } /** * Move assignment, the other value is left empty (JsonType::Null). * * @param other the other value */ inline JsonValue &operator=(JsonValue &&other) { m_handle = std::move(other.m_handle); other.m_handle = Handle(json_null(), json_decref); return *this; } /** * Create a JsonValue from a native Jansson type. This function * will increment the json_t reference count. * * @param json the value */ inline JsonValue(json_t *json) noexcept : m_handle(json, json_decref) { } /** * Construct a null value from a nullptr argument. */ inline JsonValue(std::nullptr_t) noexcept : m_handle(json_null(), json_decref) { } /** * Create an empty value (JsonType::Null). */ inline JsonValue() : m_handle(json_null(), json_decref) { } /** * Create a boolean value. * * @param value the value */ 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) { } /** * 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) { } /** * Create from a C string (JsonType::String). * * @param value the string */ 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 ~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()); } /** * 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 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 */ 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 JsonArray * @brief Manipulate JSON arrays */ 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; friend class JsonArray; 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; friend class JsonArray; 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: /** * 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. * * @return the iterator */ inline const_iterator begin() const noexcept { return const_iterator(*this, 0); } /** * Overloaded function. * * @return the iterator */ 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()); } /** * 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; /** * Erase the array content. */ inline void clear() noexcept { json_array_clear(m_handle.get()); } /** * Remove the element at the specified index. * * @param index the index */ inline void erase(int index) noexcept { json_array_remove(m_handle.get(), index); } /** * Overloaded function. * * @param it the iterator */ inline void erase(iterator it) noexcept { erase(it.m_index); } /** * 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 */ 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. * * @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 */ 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 JsonObject * @brief Object wrapper */ 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>; friend class JsonObject; 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 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<std::string, JsonValue>; friend class JsonObject; 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 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 JsonValue::JsonValue; public: /** * 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. * * @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 */ 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()); } /** * Remove all elements from the object. */ inline void clear() noexcept { json_object_clear(m_handle.get()); } /** * Remove element `key' if exists. * * @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 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 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 */ 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_