#ifndef _NLOHMANN_JSON #define _NLOHMANN_JSON #include #include #include #include #include #include #include #include #include #include #include #include #include /*! - ObjectType trick from http://stackoverflow.com/a/9860911 */ /* template struct container_resizable : std::false_type {}; template struct container_resizable : std::true_type {}; */ /*! @see https://github.com/nlohmann */ namespace nlohmann { /*! @brief JSON @tparam ObjectType type for JSON objects (@c std::map by default) @tparam ArrayType type for JSON arrays (@c std::vector by default) @tparam StringType type for JSON strings and object keys (@c std::string by default) @tparam BooleanType type for JSON booleans (@c bool by default) @tparam NumberIntegerType type for JSON integer numbers (@c int64_t by default) @tparam NumberFloatType type for JSON floating point numbers (@c double by default) */ template < template class ObjectType = std::map, //template class ArrayType = std::vector, template class ArrayType = std::vector, class StringType = std::string, class BooleanType = bool, class NumberIntegerType = int64_t, class NumberFloatType = double > class basic_json { public: ///////////////////// // container types // ///////////////////// class iterator; class const_iterator; /// the type of elements in a basic_json container using value_type = basic_json; /// the type of an element reference using reference = basic_json&; /// the type of an element const reference using const_reference = const basic_json&; /// the type of an element pointer using pointer = basic_json*; /// the type of an element const pointer using const_pointer = const basic_json*; /// a type to represent differences between iterators using difference_type = std::ptrdiff_t; /// a type to represent container sizes using size_type = std::size_t; /// an iterator for a basic_json container using iterator = basic_json::iterator; /// a const iterator for a basic_json container using const_iterator = basic_json::const_iterator; /////////////////////////// // JSON value data types // /////////////////////////// /// a type for an object using object_t = ObjectType; /// a type for an array using array_t = ArrayType; /// a type for a string using string_t = StringType; /// a type for a boolean using boolean_t = BooleanType; /// a type for a number (integer) using number_integer_t = NumberIntegerType; /// a type for a number (floating point) using number_float_t = NumberFloatType; /// a type for list initialization using list_init_t = std::initializer_list; //////////////////////// // JSON value storage // //////////////////////// /// a JSON value union json_value { /// object (stored with pointer to save storage) object_t* object; /// array (stored with pointer to save storage) array_t* array; /// string (stored with pointer to save storage) string_t* string; /// bolean boolean_t boolean; /// number (integer) number_integer_t number_integer; /// number (floating point) number_float_t number_float; /// default constructor (for null values) json_value() = default; /// constructor for objects json_value(object_t* v) : object(v) {} /// constructor for arrays json_value(array_t* v) : array(v) {} /// constructor for strings json_value(string_t* v) : string(v) {} /// constructor for booleans json_value(boolean_t v) : boolean(v) {} /// constructor for numbers (integer) json_value(number_integer_t v) : number_integer(v) {} /// constructor for numbers (floating point) json_value(number_float_t v) : number_float(v) {} }; ///////////////////////////////// // JSON value type enumeration // ///////////////////////////////// /// JSON value type enumeration enum class value_t : uint8_t { /// null value null, /// object (unordered set of name/value pairs) object, /// array (ordered collection of values) array, /// string value string, /// boolean value boolean, /// number value (integer) number_integer, /// number value (floating point) number_float }; ////////////////// // constructors // ////////////////// /// create an empty value with a given type inline basic_json(const value_t value) : m_type(value) { switch (m_type) { case (value_t::null): { break; } case (value_t::object): { m_value.object = new object_t(); break; } case (value_t::array): { m_value.array = new array_t(); break; } case (value_t::string): { m_value.string = new string_t(); break; } case (value_t::boolean): { m_value.boolean = boolean_t(); break; } case (value_t::number_integer): { m_value.number_integer = number_integer_t(); break; } case (value_t::number_float): { m_value.number_float = number_float_t(); break; } } } /// create a null object (implicitly) inline basic_json() noexcept : m_type(value_t::null) {} /// create a null object (explicitly) inline basic_json(std::nullptr_t) noexcept : m_type(value_t::null) {} /// create an object (explicit) inline basic_json(const object_t& value) : m_type(value_t::object), m_value(new object_t(value)) {} /// create an object (implicit) template ::value and std::is_constructible::value, int>::type = 0> inline basic_json(const V& value) : m_type(value_t::object), m_value(new object_t(value.begin(), value.end())) {} /// create an array (explicit) inline basic_json(const array_t& value) : m_type(value_t::array), m_value(new array_t(value)) {} /// create an array (implicit) template ::value and not std::is_same::value and std::is_constructible::value, int>::type = 0> inline basic_json(const V& value) : m_type(value_t::array), m_value(new array_t(value.begin(), value.end())) {} /// create a string (explicit) inline basic_json(const string_t& value) : m_type(value_t::string), m_value(new string_t(value)) {} /// create a string (explicit) inline basic_json(const typename string_t::value_type* value) : m_type(value_t::string), m_value(new string_t(value)) {} /// create a string (implicit) template ::value, int>::type = 0> inline basic_json(const V& value) : basic_json(string_t(value)) {} /// create a boolean (explicit) inline basic_json(boolean_t value) : m_type(value_t::boolean), m_value(value) {} /// create an integer number (explicit) inline basic_json(const number_integer_t& value) : m_type(value_t::number_integer), m_value(value) {} /// create an integer number (implicit) template::value and std::numeric_limits::is_integer, T>::type = 0> inline basic_json(const T value) noexcept : m_type(value_t::number_integer), m_value(number_integer_t(value)) {} /// create a floating point number (explicit) inline basic_json(const number_float_t& value) : m_type(value_t::number_float), m_value(value) {} /// create a floating point number (implicit) template::value and std::is_floating_point::value>::type > inline basic_json(const T value) noexcept : m_type(value_t::number_float), m_value(number_float_t(value)) {} inline basic_json(list_init_t l, bool type_deduction = true, value_t manual_type = value_t::array) { // the initializer list could describe an object bool is_object = true; // check if each element is an array with two elements whose first element // is a string for (const auto& element : l) { if ((element.m_final and element.m_type == value_t::array) or (element.m_type != value_t::array or element.size() != 2 or element[0].m_type != value_t::string)) { // we found an element that makes it impossible to use the // initializer list as object is_object = false; break; } } // adjust type if type deduction is not wanted if (not type_deduction) { // mark this object's type as final m_final = true; // if array is wanted, do not create an object though possible if (manual_type == value_t::array) { is_object = false; } // if object is wanted but impossible, throw an exception if (manual_type == value_t::object and not is_object) { throw std::logic_error("cannot create JSON object from initializer list"); } } if (is_object) { // the initializer list is a list of pairs -> create object m_type = value_t::object; m_value = new object_t(); for (auto& element : l) { m_value.object->emplace(std::move(*(element[0].m_value.string)), std::move(element[1])); } } else { // the initializer list describes an array -> create array m_type = value_t::array; m_value = new array_t(std::move(l)); } } inline static basic_json array(list_init_t l = list_init_t()) { return basic_json(l, false, value_t::array); } inline static basic_json object(list_init_t l = list_init_t()) { return basic_json(l, false, value_t::object); } /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// /// copy constructor inline basic_json(const basic_json& other) : m_type(other.m_type) { switch (m_type) { case (value_t::null): { break; } case (value_t::object): { m_value.object = new object_t(*other.m_value.object); break; } case (value_t::array): { m_value.array = new array_t(*other.m_value.array); break; } case (value_t::string): { m_value.string = new string_t(*other.m_value.string); break; } case (value_t::boolean): { m_value.boolean = other.m_value.boolean; break; } case (value_t::number_integer): { m_value.number_integer = other.m_value.number_integer; break; } case (value_t::number_float): { m_value.number_float = other.m_value.number_float; break; } } } /// move constructor inline basic_json(basic_json&& other) noexcept : m_type(std::move(other.m_type)), m_value(std::move(other.m_value)) { // invaludate payload other.m_type = value_t::null; other.m_value = {}; } /// copy assignment inline reference operator=(basic_json other) noexcept { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); return *this; } /// destructor inline ~basic_json() noexcept { switch (m_type) { case (value_t::object): { delete m_value.object; m_value.object = nullptr; break; } case (value_t::array): { delete m_value.array; m_value.array = nullptr; break; } case (value_t::string): { delete m_value.string; m_value.string = nullptr; break; } case (value_t::null): case (value_t::boolean): case (value_t::number_integer): case (value_t::number_float): { break; } } } public: /////////////////////// // object inspection // /////////////////////// /*! Serialization function for JSON objects. The function tries to mimick Python's @p json.dumps() function, and currently supports its @p indent parameter. @param indent if indent is nonnegative, then array elements and object members will be pretty-printed with that indent level. An indent level of 0 will only insert newlines. -1 (the default) selects the most compact representation @see https://docs.python.org/2/library/json.html#json.dump */ inline string_t dump(int indent = -1) const noexcept { if (indent >= 0) { return dump(true, static_cast(indent)); } else { return dump(false, 0); } } /// return the type of the object explicitly inline value_t type() const noexcept { return m_type; } /// return the type of the object implicitly operator value_t() const noexcept { return m_type; } ////////////////////// // value conversion // ////////////////////// /// get an object template ::value and std::is_constructible::value, int>::type = 0> inline T get() const { switch (m_type) { case (value_t::object): return T(m_value.object->begin(), m_value.object->end()); default: throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); } } /// get an array template ::value and std::is_constructible::value, int>::type = 0> inline T get() const { switch (m_type) { case (value_t::array): return T(m_value.array->begin(), m_value.array->end()); default: throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); } } /// get a string template ::value, int>::type = 0> inline T get() const { switch (m_type) { case (value_t::string): return *m_value.string; default: throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); } } /// get a boolean template ::value, int>::type = 0> inline T get() const { switch (m_type) { case (value_t::boolean): return m_value.boolean; default: throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); } } /// explicitly get a number template::value and std::is_arithmetic::value, int>::type = 0> inline T get() const { switch (m_type) { case (value_t::number_integer): return static_cast(m_value.number_integer); case (value_t::number_float): return static_cast(m_value.number_float); default: throw std::logic_error("cannot cast " + type_name() + " to " + typeid(T).name()); } } /// explicitly get a value template inline operator T() const { return get(); } //////////////////// // element access // //////////////////// /// access specified element with bounds checking inline reference at(size_type pos) { // at only works for arrays if (m_type != value_t::array) { throw std::runtime_error("cannot use at with " + type_name()); } return m_value.array->at(pos); } /// access specified element with bounds checking inline const_reference at(size_type pos) const { // at only works for arrays if (m_type != value_t::array) { throw std::runtime_error("cannot use at with " + type_name()); } return m_value.array->at(pos); } /// access specified element inline reference operator[](size_type pos) { // at only works for arrays if (m_type != value_t::array) { throw std::runtime_error("cannot use [] with " + type_name()); } return m_value.array->operator[](pos); } /// access specified element inline const_reference operator[](size_type pos) const { // at only works for arrays if (m_type != value_t::array) { throw std::runtime_error("cannot use [] with " + type_name()); } return m_value.array->operator[](pos); } /// access specified element with bounds checking inline reference at(const typename object_t::key_type& key) { // at only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use at with " + type_name()); } return m_value.object->at(key); } /// access specified element with bounds checking inline const_reference at(const typename object_t::key_type& key) const { // at only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use at with " + type_name()); } return m_value.object->at(key); } /// access specified element inline reference operator[](const typename object_t::key_type& key) { // at only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use [] with " + type_name()); } return m_value.object->operator[](key); } /// access specified element (needed for clang) template inline reference operator[](const T (&key)[n]) { // at only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use [] with " + type_name()); } return m_value.object->operator[](key); } /// access specified element inline reference operator[](typename object_t::key_type&& key) { // at only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use [] with " + type_name()); } return m_value.object->operator[](std::move(key)); } /////////////// // iterators // /////////////// /// returns an iterator to the beginning of the container inline iterator begin() noexcept { iterator result(this); result.set_begin(); return result; } /// returns a const iterator to the beginning of the container inline const_iterator begin() const noexcept { const_iterator result(this); result.set_begin(); return result; } /// returns a const iterator to the beginning of the container inline const_iterator cbegin() const noexcept { const_iterator result(this); result.set_begin(); return result; } /// returns an iterator to the end of the container inline iterator end() noexcept { iterator result(this); result.set_end(); return result; } /// returns a const iterator to the end of the container inline const_iterator end() const noexcept { const_iterator result(this); result.set_end(); return result; } /// returns a const iterator to the end of the container inline const_iterator cend() const noexcept { const_iterator result(this); result.set_end(); return result; } ////////////// // capacity // ////////////// /// checks whether the container is empty inline bool empty() const noexcept { switch (m_type) { case (value_t::null): { return true; } case (value_t::number_integer): case (value_t::number_float): case (value_t::boolean): case (value_t::string): { return false; } case (value_t::array): { return m_value.array->empty(); } case (value_t::object): { return m_value.object->empty(); } } } /// returns the number of elements inline size_type size() const noexcept { switch (m_type) { case (value_t::null): { return 0; } case (value_t::number_integer): case (value_t::number_float): case (value_t::boolean): case (value_t::string): { return 1; } case (value_t::array): { return m_value.array->size(); } case (value_t::object): { return m_value.object->size(); } } } /// returns the maximum possible number of elements inline size_type max_size() const noexcept { switch (m_type) { case (value_t::null): { return 0; } case (value_t::number_integer): case (value_t::number_float): case (value_t::boolean): case (value_t::string): { return 1; } case (value_t::array): { return m_value.array->max_size(); } case (value_t::object): { return m_value.object->max_size(); } } } /////////////// // modifiers // /////////////// /// clears the contents inline void clear() noexcept { switch (m_type) { case (value_t::null): { break; } case (value_t::number_integer): { m_value.number_integer = {}; break; } case (value_t::number_float): { m_value.number_float = {}; break; } case (value_t::boolean): { m_value.boolean = {}; break; } case (value_t::string): { m_value.string->clear(); break; } case (value_t::array): { m_value.array->clear(); break; } case (value_t::object): { m_value.object->clear(); break; } } } /// add an object to an array inline void push_back(basic_json&& value) { // push_back only works for null objects or arrays if (not(m_type == value_t::null or m_type == value_t::array)) { throw std::runtime_error("cannot add element to " + type_name()); } // transform null object into an array if (m_type == value_t::null) { m_type = value_t::array; m_value.array = new array_t; } // add element to array (move semantics) m_value.array->push_back(std::move(value)); // invalidate object value.m_type = value_t::null; } /// add an object to an array inline void push_back(const basic_json& value) { // push_back only works for null objects or arrays if (not(m_type == value_t::null or m_type == value_t::array)) { throw std::runtime_error("cannot add element to " + type_name()); } // transform null object into an array if (m_type == value_t::null) { m_type = value_t::array; m_value.array = new array_t; } // add element to array m_value.array->push_back(value); } /* /// add an object to an array inline reference operator+=(const basic_json& value) { push_back(value); return *this; } */ /// add constructible objects to an array template::value>::type = 0> inline void push_back(const T& value) { assert(false); // not sure if function will ever be called push_back(basic_json(value)); } /* /// add constructible objects to an array template::value>::type = 0> inline reference operator+=(const T& value) { push_back(basic_json(value)); return *this; } */ /// add an object to an object inline void push_back(const typename object_t::value_type& value) { // push_back only works for null objects or objects if (not(m_type == value_t::null or m_type == value_t::object)) { throw std::runtime_error("cannot add element to " + type_name()); } // transform null object into an object if (m_type == value_t::null) { m_type = value_t::object; m_value.object = new object_t; } // add element to array m_value.object->insert(value); } /* /// add an object to an object inline reference operator+=(const typename object_t::value_type& value) { push_back(value); return operator[](value.first); } */ /// constructs element in-place at the end of an array template ::value, int>::type = 0> inline void emplace_back(T && arg) { // push_back only works for null objects or arrays if (not(m_type == value_t::null or m_type == value_t::array)) { throw std::runtime_error("cannot add element to " + type_name()); } // transform null object into an array if (m_type == value_t::null) { m_type = value_t::array; m_value.array = new array_t; } // add element to array m_value.array->emplace_back(std::forward(arg)); } /// swaps the contents inline void swap(reference other) noexcept { std::swap(m_type, other.m_type); std::swap(m_value, other.m_value); } /// swaps the contents inline void swap(array_t& other) { // swap only works for arrays if (m_type != value_t::array) { throw std::runtime_error("cannot use swap with " + type_name()); } // swap arrays std::swap(*(m_value.array), other); } /// swaps the contents inline void swap(object_t& other) { // swap only works for objects if (m_type != value_t::object) { throw std::runtime_error("cannot use swap with " + type_name()); } // swap arrays std::swap(*(m_value.object), other); } /// swaps the contents inline void swap(string_t& other) { // swap only works for strings if (m_type != value_t::string) { throw std::runtime_error("cannot use swap with " + type_name()); } // swap arrays std::swap(*(m_value.string), other); } ////////////////////////////////////////// // lexicographical comparison operators // ////////////////////////////////////////// /// comparison: equal friend bool operator==(const_reference lhs, const_reference rhs) { switch (lhs.type()) { case (value_t::array): { if (rhs.type() == value_t::array) { return *lhs.m_value.array == *rhs.m_value.array; } break; } case (value_t::object): { if (rhs.type() == value_t::object) { return *lhs.m_value.object == *rhs.m_value.object; } break; } case (value_t::null): { if (rhs.type() == value_t::null) { return true; } break; } case (value_t::string): { if (rhs.type() == value_t::string) { return *lhs.m_value.string == *rhs.m_value.string; } break; } case (value_t::boolean): { if (rhs.type() == value_t::boolean) { return lhs.m_value.boolean == rhs.m_value.boolean; } break; } case (value_t::number_integer): { if (rhs.type() == value_t::number_integer) { return lhs.m_value.number_integer == rhs.m_value.number_integer; } if (rhs.type() == value_t::number_float) { return lhs.m_value.number_integer == static_cast(rhs.m_value.number_float); } break; } case (value_t::number_float): { if (rhs.type() == value_t::number_integer) { return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); } if (rhs.type() == value_t::number_float) { return lhs.m_value.number_float == rhs.m_value.number_float; } break; } } return false; } /// comparison: not equal friend bool operator!=(const_reference lhs, const_reference rhs) { return not (lhs == rhs); } /// comparison: less than friend bool operator<(const_reference lhs, const_reference rhs) { switch (lhs.type()) { case (value_t::array): { if (rhs.type() == value_t::array) { return *lhs.m_value.array < *rhs.m_value.array; } break; } case (value_t::object): { if (rhs.type() == value_t::object) { return *lhs.m_value.object < *rhs.m_value.object; } break; } case (value_t::null): { if (rhs.type() == value_t::null) { return false; } break; } case (value_t::string): { if (rhs.type() == value_t::string) { return *lhs.m_value.string < *rhs.m_value.string; } break; } case (value_t::boolean): { if (rhs.type() == value_t::boolean) { return lhs.m_value.boolean < rhs.m_value.boolean; } break; } case (value_t::number_integer): { if (rhs.type() == value_t::number_integer) { return lhs.m_value.number_integer < rhs.m_value.number_integer; } if (rhs.type() == value_t::number_float) { return lhs.m_value.number_integer < static_cast(rhs.m_value.number_float); } break; } case (value_t::number_float): { if (rhs.type() == value_t::number_integer) { return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); } if (rhs.type() == value_t::number_float) { return lhs.m_value.number_float < rhs.m_value.number_float; } break; } } return false; } /// comparison: less than or equal friend bool operator<=(const_reference lhs, const_reference rhs) { return not (rhs < lhs); } /// comparison: greater than friend bool operator>(const_reference lhs, const_reference rhs) { return not (lhs <= rhs); } /// comparison: greater than or equal friend bool operator>=(const_reference lhs, const_reference rhs) { return not (lhs < rhs); } /////////////////// // serialization // /////////////////// /// serialize to stream friend std::ostream& operator<<(std::ostream& o, const basic_json& j) { o << j.dump(); return o; } /// serialize to stream friend std::ostream& operator>>(const basic_json& j, std::ostream& o) { o << j.dump(); return o; } private: /////////////////////////// // convenience functions // /////////////////////////// /// return the type as string inline string_t type_name() const noexcept { switch (m_type) { case (value_t::null): { return "null"; } case (value_t::object): { return "object"; } case (value_t::array): { return "array"; } case (value_t::string): { return "string"; } case (value_t::boolean): { return "boolean"; } case (value_t::number_integer): case (value_t::number_float): { return "number"; } } } /*! Escape a string by replacing special characters by a sequence of an escape character (backslash) and another character. */ static string_t escape_string(const string_t& s) { // create a result string of at least the size than s string_t result; result.reserve(s.size()); for (auto c : s) { switch (c) { // quotation mark case '"': { result.append("\\\"", 2); break; } // reverse solidus case '\\': { result.append("\\\\", 2); break; } // backspace case '\b': { result.append("\\b", 2); break; } // formfeed case '\f': { result.append("\\f", 2); break; } // newline case '\n': { result.append("\\n", 2); break; } // carriage return case '\r': { result.append("\\r", 2); break; } // horizontal tab case '\t': { result.append("\\t", 2); break; } default: { result.append(1, c); } } } return result; } /*! Internal implementation of the serialization function. @param prettyPrint whether the output shall be pretty-printed @param indentStep the indent level @param currentIndent the current indent level (only used internally) */ inline string_t dump(const bool prettyPrint, const unsigned int indentStep, unsigned int currentIndent = 0) const noexcept { // helper function to return whitespace as indentation const auto indent = [prettyPrint, ¤tIndent]() { return prettyPrint ? string_t(currentIndent, ' ') : string_t(); }; switch (m_type) { case (value_t::null): { return "null"; } case (value_t::object): { if (m_value.object->empty()) { return "{}"; } string_t result = "{"; // increase indentation if (prettyPrint) { currentIndent += indentStep; result += "\n"; } for (typename object_t::const_iterator i = m_value.object->begin(); i != m_value.object->end(); ++i) { if (i != m_value.object->begin()) { result += prettyPrint ? ",\n" : ","; } result += indent() + "\"" + i->first + "\":" + (prettyPrint ? " " : "") + i->second.dump(prettyPrint, indentStep, currentIndent); } // decrease indentation if (prettyPrint) { currentIndent -= indentStep; result += "\n"; } return result + indent() + "}"; } case (value_t::array): { if (m_value.array->empty()) { return "[]"; } string_t result = "["; // increase indentation if (prettyPrint) { currentIndent += indentStep; result += "\n"; } for (typename array_t::const_iterator i = m_value.array->begin(); i != m_value.array->end(); ++i) { if (i != m_value.array->begin()) { result += prettyPrint ? ",\n" : ","; } result += indent() + i->dump(prettyPrint, indentStep, currentIndent); } // decrease indentation if (prettyPrint) { currentIndent -= indentStep; result += "\n"; } return result + indent() + "]"; } case (value_t::string): { return string_t("\"") + escape_string(*m_value.string) + "\""; } case (value_t::boolean): { return m_value.boolean ? "true" : "false"; } case (value_t::number_integer): { return std::to_string(m_value.number_integer); } case (value_t::number_float): { return std::to_string(m_value.number_float); } } } private: ////////////////////// // member variables // ////////////////////// /// the type of the current element value_t m_type = value_t::null; /// whether the type of JSON object may change later bool m_final = false; /// the value of the current element json_value m_value = {}; public: /////////////// // iterators // /////////////// /// a bidirectional iterator for the basic_json class class iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced using value_type = basic_json::value_type; /// a type to represent differences between iterators using difference_type = basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = basic_json::pointer; /// defines a reference to the type iterated over (value_type) using reference = basic_json::reference; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// values of a generic iterator type of non-container JSON values enum class generic_iterator_value { /// the iterator was not initialized uninitialized, /// the iterator points to the only value begin, /// the iterator points past the only value end }; /// an iterator value union internal_iterator { /// iterator for JSON objects typename object_t::iterator object_iterator; /// iterator for JSON arrays typename array_t::iterator array_iterator; /// generic iteraotr for all other value types generic_iterator_value generic_iterator; /// default constructor internal_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} /// constructor for object iterators internal_iterator(typename object_t::iterator v) : object_iterator(v) {} /// constructor for array iterators internal_iterator(typename array_t::iterator v) : array_iterator(v) {} /// constructor for generic iterators internal_iterator(generic_iterator_value v) : generic_iterator(v) {} }; /// constructor for a given JSON instance inline iterator(pointer object) : m_object(object) { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = typename object_t::iterator(); break; } case (basic_json::value_t::array): { m_it.array_iterator = typename array_t::iterator(); break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } } /// copy assignment inline iterator& operator=(const iterator& other) noexcept { m_object = other.m_object; m_it = other.m_it; return *this; } /// set the iterator to the first value inline void set_begin() noexcept { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = m_object->m_value.object->begin(); break; } case (basic_json::value_t::array): { m_it.array_iterator = m_object->m_value.array->begin(); break; } case (basic_json::value_t::null): { m_it.generic_iterator = generic_iterator_value::end; break; } default: { m_it.generic_iterator = generic_iterator_value::begin; break; } } } /// set the iterator past the last value inline void set_end() noexcept { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = m_object->m_value.object->end(); break; } case (basic_json::value_t::array): { m_it.array_iterator = m_object->m_value.array->end(); break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } } /// return a reference to the value pointed to by the iterator inline reference operator*() const { switch (m_object->m_type) { case (basic_json::value_t::object): { return m_it.object_iterator->second; } case (basic_json::value_t::array): { return *m_it.array_iterator; } case (basic_json::value_t::null): { throw std::out_of_range("cannot get value"); } default: { if (m_it.generic_iterator == generic_iterator_value::begin) { return *m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// dereference the iterator inline pointer operator->() const { switch (m_object->m_type) { case (basic_json::value_t::object): { return &(m_it.object_iterator->second); } case (basic_json::value_t::array): { return &*m_it.array_iterator; } default: { if (m_it.generic_iterator == generic_iterator_value::begin) { return m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// post-increment (it++) inline iterator operator++(int) { iterator result = *this; switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator++; break; } case (basic_json::value_t::array): { m_it.array_iterator++; break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } return result; } /// pre-increment (++it) inline iterator& operator++() { switch (m_object->m_type) { case (basic_json::value_t::object): { ++m_it.object_iterator; break; } case (basic_json::value_t::array): { ++m_it.array_iterator; break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } return *this; } /// post-decrement (it--) inline iterator operator--(int) { iterator result = *this; switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator--; break; } case (basic_json::value_t::array): { m_it.array_iterator--; break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } return result; } /// pre-decrement (--it) inline iterator& operator--() { switch (m_object->m_type) { case (basic_json::value_t::object): { --m_it.object_iterator; break; } case (basic_json::value_t::array): { --m_it.array_iterator; break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } return *this; } /// comparison: equal inline bool operator==(const iterator& other) const { if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) { return false; } switch (m_object->m_type) { case (basic_json::value_t::object): { return (m_it.object_iterator == other.m_it.object_iterator); } case (basic_json::value_t::array): { return (m_it.array_iterator == other.m_it.array_iterator); } default: { return (m_it.generic_iterator == other.m_it.generic_iterator); } } } /// comparison: not equal inline bool operator!=(const iterator& other) const { return not operator==(other); } private: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance internal_iterator m_it; }; /// a const bidirectional iterator for the basic_json class class const_iterator : public std::iterator { public: /// the type of the values when the iterator is dereferenced using value_type = basic_json::value_type; /// a type to represent differences between iterators using difference_type = basic_json::difference_type; /// defines a pointer to the type iterated over (value_type) using pointer = basic_json::const_pointer; /// defines a reference to the type iterated over (value_type) using reference = basic_json::const_reference; /// the category of the iterator using iterator_category = std::bidirectional_iterator_tag; /// values of a generic iterator type of non-container JSON values enum class generic_iterator_value { /// the iterator was not initialized uninitialized, /// the iterator points to the only value begin, /// the iterator points past the only value end }; /// an iterator value union internal_const_iterator { /// iterator for JSON objects typename object_t::const_iterator object_iterator; /// iterator for JSON arrays typename array_t::const_iterator array_iterator; /// generic iteraotr for all other value types generic_iterator_value generic_iterator; /// default constructor internal_const_iterator() : generic_iterator(generic_iterator_value::uninitialized) {} /// constructor for object iterators internal_const_iterator(typename object_t::iterator v) : object_iterator(v) {} /// constructor for array iterators internal_const_iterator(typename array_t::iterator v) : array_iterator(v) {} /// constructor for generic iterators internal_const_iterator(generic_iterator_value v) : generic_iterator(v) {} }; /// constructor for a given JSON instance inline const_iterator(pointer object) : m_object(object) { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = typename object_t::const_iterator(); break; } case (basic_json::value_t::array): { m_it.array_iterator = typename array_t::const_iterator(); break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } } inline const_iterator(const iterator& other) : m_object(other.m_object), m_it(other.m_it) {} /// copy assignment inline const_iterator operator=(const const_iterator& other) noexcept { m_object = other.m_object; m_it = other.m_it; return *this; } /// set the iterator to the first value inline void set_begin() noexcept { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = m_object->m_value.object->cbegin(); break; } case (basic_json::value_t::array): { m_it.array_iterator = m_object->m_value.array->cbegin(); break; } case (basic_json::value_t::null): { m_it.generic_iterator = generic_iterator_value::end; break; } default: { m_it.generic_iterator = generic_iterator_value::begin; break; } } } /// set the iterator past the last value inline void set_end() noexcept { switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator = m_object->m_value.object->cend(); break; } case (basic_json::value_t::array): { m_it.array_iterator = m_object->m_value.array->cend(); break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } } /// return a reference to the value pointed to by the iterator inline reference operator*() const { switch (m_object->m_type) { case (basic_json::value_t::object): { return m_it.object_iterator->second; } case (basic_json::value_t::array): { return *m_it.array_iterator; } case (basic_json::value_t::null): { throw std::out_of_range("cannot get value"); } default: { if (m_it.generic_iterator == generic_iterator_value::begin) { return *m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// dereference the iterator inline pointer operator->() const { switch (m_object->m_type) { case (basic_json::value_t::object): { return &(m_it.object_iterator->second); } case (basic_json::value_t::array): { return &*m_it.array_iterator; } default: { if (m_it.generic_iterator == generic_iterator_value::begin) { return m_object; } else { throw std::out_of_range("cannot get value"); } } } } /// post-increment (it++) inline const_iterator operator++(int) { const_iterator result = *this; switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator++; break; } case (basic_json::value_t::array): { m_it.array_iterator++; break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } return result; } /// pre-increment (++it) inline const_iterator& operator++() { switch (m_object->m_type) { case (basic_json::value_t::object): { ++m_it.object_iterator; break; } case (basic_json::value_t::array): { ++m_it.array_iterator; break; } default: { m_it.generic_iterator = generic_iterator_value::end; break; } } return *this; } /// post-decrement (it--) inline const_iterator operator--(int) { iterator result = *this; switch (m_object->m_type) { case (basic_json::value_t::object): { m_it.object_iterator--; break; } case (basic_json::value_t::array): { m_it.array_iterator--; break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } return result; } /// pre-decrement (--it) inline const_iterator& operator--() { switch (m_object->m_type) { case (basic_json::value_t::object): { --m_it.object_iterator; break; } case (basic_json::value_t::array): { --m_it.array_iterator; break; } default: { m_it.generic_iterator = generic_iterator_value::uninitialized; break; } } return *this; } /// comparison: equal inline bool operator==(const const_iterator& other) const { if (m_object != other.m_object or m_object->m_type != other.m_object->m_type) { return false; } switch (m_object->m_type) { case (basic_json::value_t::object): { return (m_it.object_iterator == other.m_it.object_iterator); } case (basic_json::value_t::array): { return (m_it.array_iterator == other.m_it.array_iterator); } default: { return (m_it.generic_iterator == other.m_it.generic_iterator); } } } /// comparison: not equal inline bool operator!=(const const_iterator& other) const { return not operator==(other); } private: /// associated JSON instance pointer m_object = nullptr; /// the actual iterator of the associated instance internal_const_iterator m_it; }; }; ///////////// // presets // ///////////// /// default JSON class using json = basic_json<>; } ///////////////////////// // nonmember functions // ///////////////////////// // specialization of std::swap, and std::hash namespace std { /// swaps the values of two JSON objects template <> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value and is_nothrow_move_assignable::value ) { j1.swap(j2); } /// hash value for JSON objects template <> struct hash { size_t operator()(const nlohmann::json& j) const { // a naive hashing via the string representation return hash()(j.dump()); } }; } #endif