diff --git a/src/json.hpp b/src/json.hpp index e2f1cfa9c2..71e37b3432 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -4561,8521 +4561,8566 @@ class json_reverse_iterator : public std::reverse_iterator return it.operator * (); } }; -} // namespace detail -/// namespace to hold default `to_json` / `from_json` functions -namespace -{ -constexpr const auto& to_json = detail::static_const::value; -constexpr const auto& from_json = detail::static_const::value; -} +///////////////////// +// output adapters // +///////////////////// +/// abstract output adapter interface +template class output_adapter +{ + public: + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, size_t length) = 0; + virtual ~output_adapter() {} +}; -/*! -@brief default JSONSerializer template argument +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; -This serializer ignores the template arguments and uses ADL -([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) -for serialization. -*/ -template -struct adl_serializer +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter { - /*! - @brief convert a JSON value to any value type - - This function is usually called by the `get()` function of the - @ref basic_json class (either explicit or via conversion operators). + public: + output_vector_adapter(std::vector& vec) : v(vec) {} - @param[in] j JSON value to read from - @param[in,out] val value to write to - */ - template - static void from_json(BasicJsonType&& j, ValueType& val) noexcept( - noexcept(::nlohmann::from_json(std::forward(j), val))) + void write_character(CharType c) override { - ::nlohmann::from_json(std::forward(j), val); + v.push_back(c); } - /*! - @brief convert any value type to a JSON value - - This function is usually called by the constructors of the @ref basic_json - class. - - @param[in,out] j JSON value to write to - @param[in] val value to read from - */ - template - static void to_json(BasicJsonType& j, ValueType&& val) noexcept( - noexcept(::nlohmann::to_json(j, std::forward(val)))) + void write_characters(const CharType* s, size_t length) override { - ::nlohmann::to_json(j, std::forward(val)); + std::copy(s, s + length, std::back_inserter(v)); } -}; - -/*! -@brief JSON Pointer - -A JSON pointer defines a string syntax for identifying a specific value -within a JSON document. It can be used with functions `at` and -`operator[]`. Furthermore, JSON pointers are the base for JSON patches. -@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + private: + std::vector& v; +}; -@since version 2.0.0 -*/ -class json_pointer +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter { - /// allow basic_json to access private members - NLOHMANN_BASIC_JSON_TPL_DECLARATION - friend class basic_json; - public: - /*! - @brief create JSON pointer - - Create a JSON pointer according to the syntax described in - [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - - @param[in] s string representing the JSON pointer; if omitted, the - empty string is assumed which references the whole JSON - value - - @throw parse_error.107 if the given JSON pointer @a s is nonempty and - does not begin with a slash (`/`); see example below - - @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s - is not followed by `0` (representing `~`) or `1` (representing `/`); - see example below - - @liveexample{The example shows the construction several valid JSON - pointers as well as the exceptional behavior.,json_pointer} - - @since version 2.0.0 - */ - explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} + output_stream_adapter(std::basic_ostream& s) : stream(s) {} - /*! - @brief return a string representation of the JSON pointer + void write_character(CharType c) override + { + stream.put(c); + } - @invariant For each JSON pointer `ptr`, it holds: - @code {.cpp} - ptr == json_pointer(ptr.to_string()); - @endcode + void write_characters(const CharType* s, size_t length) override + { + stream.write(s, static_cast(length)); + } - @return a string representation of the JSON pointer + private: + std::basic_ostream& stream; +}; - @liveexample{The example shows the result of `to_string`., - json_pointer__to_string} +/// output adapter for basic_string +template +class output_string_adapter : public output_adapter +{ + public: + output_string_adapter(std::string& s) : str(s) {} - @since version 2.0.0 - */ - std::string to_string() const noexcept + void write_character(CharType c) override { - return std::accumulate(reference_tokens.begin(), reference_tokens.end(), - std::string{}, - [](const std::string & a, const std::string & b) - { - return a + "/" + escape(b); - }); + str.push_back(c); } - /// @copydoc to_string() - operator std::string() const + void write_characters(const CharType* s, size_t length) override { - return to_string(); + str.append(s, length); } private: - /*! - @brief remove and return last reference pointer - @throw out_of_range.405 if JSON pointer has no parent - */ - std::string pop_back() - { - if (is_root()) - { - JSON_THROW( - detail::out_of_range::create(405, "JSON pointer has no parent")); - } + std::basic_string& str; +}; - auto last = reference_tokens.back(); - reference_tokens.pop_back(); - return last; +template struct output_adapter_factory +{ + static std::shared_ptr> + create(std::vector& vec) + { + return std::make_shared>(vec); } - /// return whether pointer points to the root document - bool is_root() const + static std::shared_ptr> create(std::ostream& s) { - return reference_tokens.empty(); + return std::make_shared>(s); } - json_pointer top() const + static std::shared_ptr> create(std::string& s) { - if (is_root()) - { - JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); - } - - json_pointer result = *this; - result.reference_tokens = {reference_tokens[0]}; - return result; + return std::make_shared>(s); } +}; +////////////////////////////// +// binary reader and writer // +////////////////////////////// - /*! - @brief create and return a reference to the pointed to value +/*! +@brief deserialization of CBOR and MessagePack values +*/ +template +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; - @complexity Linear in the number of reference tokens. + public: + /*! + @brief create a binary reader - @throw parse_error.109 if array index is not a number - @throw type_error.313 if value cannot be unflattened - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const; + @param[in] adapter input adapter to read from + */ + explicit binary_reader(input_adapter_t adapter) + : ia(adapter), is_little_endian(little_endianess()) + { + assert(ia); + } /*! - @brief return a reference to the pointed to value - - @note This version does not throw if a value is not present, but tries - to create nested values instead. For instance, calling this function - with pointer `"/this/that"` on a null value is equivalent to calling - `operator[]("this").operator[]("that")` on that value, effectively - changing the null value to an object. + @brief create a JSON value from CBOR input - @param[in] ptr a JSON value + @param[in] get_char whether a new character should be retrieved from + the input (true, default) or whether the last + read character should be considered instead - @return reference to the JSON value pointed to by the JSON pointer + @return JSON value created from CBOR input - @complexity Linear in the length of the JSON pointer. + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_cbor(const bool get_char = true) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const; + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + { + return static_cast(current); + } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const; + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + return get_number(); + } - /*! - @brief return a const reference to the pointed to value + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + return get_number(); + } - @param[in] ptr a JSON value + case 0x1a: // Unsigned integer (four-byte uint32_t follows) + { + return get_number(); + } + + case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + { + return get_number(); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + { + return static_cast(0x20 - 1 - current); + } + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + // must be uint8_t ! + return static_cast(-1) - get_number(); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + { + return static_cast(-1) - get_number(); + } + + case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + { + return static_cast(-1) - + static_cast(get_number()); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7f: // UTF-8 string (indefinite length) + { + return get_cbor_string(); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + { + BasicJsonType result = value_t::array; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } - @return const reference to the JSON value pointed to by the JSON - pointer + case 0x98: // array (one-byte uint8_t for n follows) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; + case 0x99: // array (two-byte uint16_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } - /*! - @throw parse_error.106 if an array index begins with '0' - @throw parse_error.109 if an array index was not a number - @throw out_of_range.402 if the array index '-' is used - @throw out_of_range.404 if the JSON pointer can not be resolved - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; + case 0x9a: // array (four-byte uint32_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } - /*! - @brief split the string input to reference tokens + case 0x9b: // array (eight-byte uint64_t for n follow) + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_cbor()); + } + return result; + } - @note This function is only called by the json_pointer constructor. - All exceptions below are documented there. + case 0x9f: // array (indefinite length) + { + BasicJsonType result = value_t::array; + while (get() != 0xff) + { + result.push_back(parse_cbor(false)); + } + return result; + } - @throw parse_error.107 if the pointer is not empty or begins with '/' - @throw parse_error.108 if character '~' is not followed by '0' or '1' - */ - static std::vector split(const std::string& reference_string) - { - std::vector result; + // map (0x00..0x17 pairs of data items follow) + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + { + BasicJsonType result = value_t::object; + const auto len = static_cast(current & 0x1f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } - // special case: empty reference string -> no reference tokens - if (reference_string.empty()) - { - return result; - } + case 0xb8: // map (one-byte uint8_t for n follows) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } - // check if nonempty reference string begins with slash - if (reference_string[0] != '/') - { - JSON_THROW(detail::parse_error::create( - 107, 1, - "JSON pointer must be empty or begin with '/' - was: '" + - reference_string + "'")); - } + case 0xb9: // map (two-byte uint16_t for n follow) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } - // extract the reference tokens: - // - slash: position of the last read slash (or end of string) - // - start: position after the previous slash - for ( - // search for the first slash after the first character - size_t slash = reference_string.find_first_of('/', 1), - // set the beginning of the first reference token - start = 1; - // we can stop if start == string::npos+1 = 0 - start != 0; - // set the beginning of the next reference token - // (will eventually be 0 if slash == std::string::npos) - start = slash + 1, - // find next slash - slash = reference_string.find_first_of('/', start)) - { - // use the text between the beginning of the reference token - // (start) and the last slash (slash). - auto reference_token = reference_string.substr(start, slash - start); + case 0xba: // map (four-byte uint32_t for n follow) + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } - // check reference tokens are properly escaped - for (size_t pos = reference_token.find_first_of('~'); - pos != std::string::npos; - pos = reference_token.find_first_of('~', pos + 1)) + case 0xbb: // map (eight-byte uint64_t for n follow) { - assert(reference_token[pos] == '~'); + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_cbor_string(); + result[key] = parse_cbor(); + } + return result; + } - // ~ must be followed by 0 or 1 - if (pos == reference_token.size() - 1 or - (reference_token[pos + 1] != '0' and - reference_token[pos + 1] != '1')) + case 0xbf: // map (indefinite length) + { + BasicJsonType result = value_t::object; + while (get() != 0xff) { - JSON_THROW(detail::parse_error::create( - 108, 0, "escape character '~' must be followed with '0' or '1'")); + auto key = get_cbor_string(); + result[key] = parse_cbor(); } + return result; } - // finally, store the reference token - unescape(reference_token); - result.push_back(reference_token); - } + case 0xf4: // false + { + return false; + } - return result; - } + case 0xf5: // true + { + return true; + } - /*! - @brief replace all occurrences of a substring by another string + case 0xf6: // null + { + return value_t::null; + } - @param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t - @param[in] f the substring to replace with @a t - @param[in] t the string to replace @a f + case 0xf9: // Half-Precision Float (two-byte IEEE 754) + { + const int byte1 = get(); + check_eof(); + const int byte2 = get(); + check_eof(); - @pre The search string @a f must not be empty. **This precondition is - enforced with an assertion.** + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const int half = (byte1 << 8) + byte2; + const int exp = (half >> 10) & 0x1f; + const int mant = half & 0x3ff; + double val; + if (exp == 0) + { + val = std::ldexp(mant, -24); + } + else if (exp != 31) + { + val = std::ldexp(mant + 1024, exp - 25); + } + else + { + val = (mant == 0) ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + } + return (half & 0x8000) != 0 ? -val : val; + } - @since version 2.0.0 - */ - static void replace_substring(std::string& s, const std::string& f, - const std::string& t) - { - assert(not f.empty()); + case 0xfa: // Single-Precision Float (four-byte IEEE 754) + { + return get_number(); + } - for (size_t pos = s.find(f); // find first occurrence of f - pos != std::string::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t - pos = s.find(f, pos + t.size()) // find next occurrence of f - ) - ; - } + case 0xfb: // Double-Precision Float (eight-byte IEEE 754) + { + return get_number(); + } - /// escape tilde and slash - static std::string escape(std::string s) - { - // escape "~"" to "~0" and "/" to "~1" - replace_substring(s, "~", "~0"); - replace_substring(s, "/", "~1"); - return s; + default: // anything else (0xFF is handled inside the other types) + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); + } + } } - /// unescape tilde and slash - static void unescape(std::string& s) + /*! + @brief create a JSON value from MessagePack input + + @return JSON value created from MessagePack input + + @throw parse_error.110 if input ended unexpectedly + @throw parse_error.112 if unsupported byte was read + */ + BasicJsonType parse_msgpack() { - // first transform any occurrence of the sequence '~1' to '/' - replace_substring(s, "~1", "/"); - // then transform any occurrence of the sequence '~0' to '~' - replace_substring(s, "~0", "~"); - } + switch (get()) + { + // EOF + case std::char_traits::eof(): + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x2e: + case 0x2f: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + case 0x3e: + case 0x3f: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + { + return static_cast(current); + } + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8a: + case 0x8b: + case 0x8c: + case 0x8d: + case 0x8e: + case 0x8f: + { + BasicJsonType result = value_t::object; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } - /*! - @param[in] reference_string the reference string to the current value - @param[in] value the value to consider - @param[in,out] result the result object to insert values to + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + { + BasicJsonType result = value_t::array; + const auto len = static_cast(current & 0x0f); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } - @note Empty objects or arrays are flattened to `null`. - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - static void flatten(const std::string& reference_string, - const NLOHMANN_BASIC_JSON_TPL& value, - NLOHMANN_BASIC_JSON_TPL& result); + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + { + return get_msgpack_string(); + } + + case 0xc0: // nil + { + return value_t::null; + } + + case 0xc2: // false + { + return false; + } - /*! - @param[in] value flattened JSON + case 0xc3: // true + { + return true; + } - @return unflattened JSON + case 0xca: // float 32 + { + return get_number(); + } - @throw parse_error.109 if array index is not a number - @throw type_error.314 if value is not an object - @throw type_error.315 if object values are not primitive - @throw type_error.313 if value cannot be unflattened - */ - NLOHMANN_BASIC_JSON_TPL_DECLARATION - static NLOHMANN_BASIC_JSON_TPL - unflatten(const NLOHMANN_BASIC_JSON_TPL& value); + case 0xcb: // float 64 + { + return get_number(); + } - friend bool operator==(json_pointer const& lhs, - json_pointer const& rhs) noexcept; + case 0xcc: // uint 8 + { + return get_number(); + } - friend bool operator!=(json_pointer const& lhs, - json_pointer const& rhs) noexcept; + case 0xcd: // uint 16 + { + return get_number(); + } - /// the reference tokens - std::vector reference_tokens; -}; + case 0xce: // uint 32 + { + return get_number(); + } -/*! -@brief a class to store JSON values + case 0xcf: // uint 64 + { + return get_number(); + } -@tparam ObjectType type for JSON objects (`std::map` by default; will be used -in @ref object_t) -@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used -in @ref array_t) -@tparam StringType type for JSON strings and object keys (`std::string` by -default; will be used in @ref string_t) -@tparam BooleanType type for JSON booleans (`bool` by default; will be used -in @ref boolean_t) -@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by -default; will be used in @ref number_integer_t) -@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c -`uint64_t` by default; will be used in @ref number_unsigned_t) -@tparam NumberFloatType type for JSON floating-point numbers (`double` by -default; will be used in @ref number_float_t) -@tparam AllocatorType type of the allocator to use (`std::allocator` by -default) -@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` -and `from_json()` (@ref adl_serializer by default) + case 0xd0: // int 8 + { + return get_number(); + } -@requirement The class satisfies the following concept requirements: -- Basic - - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): - JSON values can be default constructed. The result will be a JSON null - value. - - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): - A JSON value can be constructed from an rvalue argument. - - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): - A JSON value can be copy-constructed from an lvalue expression. - - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): - A JSON value van be assigned from an rvalue argument. - - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): - A JSON value can be copy-assigned from an lvalue expression. - - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): - JSON values can be destructed. -- Layout - - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): - JSON values have - [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): - All non-static data members are private and standard layout types, the - class has no virtual functions or (virtual) base classes. -- Library-wide - - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): - JSON values can be compared with `==`, see @ref - operator==(const_reference,const_reference). - - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): - JSON values can be compared with `<`, see @ref - operator<(const_reference,const_reference). - - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): - Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of - other compatible types, using unqualified function call @ref swap(). - - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): - JSON values can be compared against `std::nullptr_t` objects which are used - to model the `null` value. -- Container - - [Container](http://en.cppreference.com/w/cpp/concept/Container): - JSON values can be used like STL containers and provide iterator access. - - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); - JSON values can be used like STL containers and provide reverse iterator - access. + case 0xd1: // int 16 + { + return get_number(); + } -@invariant The member variables @a m_value and @a m_type have the following -relationship: -- If `m_type == value_t::object`, then `m_value.object != nullptr`. -- If `m_type == value_t::array`, then `m_value.array != nullptr`. -- If `m_type == value_t::string`, then `m_value.string != nullptr`. -The invariants are checked by member function assert_invariant(). + case 0xd2: // int 32 + { + return get_number(); + } -@internal -@note ObjectType trick from http://stackoverflow.com/a/9860911 -@endinternal + case 0xd3: // int 64 + { + return get_number(); + } -@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange -Format](http://rfc7159.net/rfc7159) + case 0xd9: // str 8 + case 0xda: // str 16 + case 0xdb: // str 32 + { + return get_msgpack_string(); + } -@since version 1.0.0 + case 0xdc: // array 16 + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } -@nosubgrouping -*/ -NLOHMANN_BASIC_JSON_TPL_DECLARATION -class basic_json -{ - private: - template friend struct detail::external_constructor; - friend ::nlohmann::detail::parser; - template - friend class ::nlohmann::detail::iter_impl; - /// workaround type for MSVC - using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + case 0xdd: // array 32 + { + BasicJsonType result = value_t::array; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + result.push_back(parse_msgpack()); + } + return result; + } - // convenience aliases for types residing in namespace detail; - using lexer = ::nlohmann::detail::lexer; - using parser = ::nlohmann::detail::parser; + case 0xde: // map 16 + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } - using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; - template - using internal_iterator = ::nlohmann::detail::internal_iterator; - template - using iter_impl = ::nlohmann::detail::iter_impl; - template - using iteration_proxy = ::nlohmann::detail::iteration_proxy; - template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + case 0xdf: // map 32 + { + BasicJsonType result = value_t::object; + const auto len = static_cast(get_number()); + for (size_t i = 0; i < len; ++i) + { + get(); + auto key = get_msgpack_string(); + result[key] = parse_msgpack(); + } + return result; + } - public: - using value_t = detail::value_t; - // forward declarations - using json_pointer = ::nlohmann::json_pointer; - template - using json_serializer = JSONSerializer; + // positive fixint + case 0xe0: + case 0xe1: + case 0xe2: + case 0xe3: + case 0xe4: + case 0xe5: + case 0xe6: + case 0xe7: + case 0xe8: + case 0xe9: + case 0xea: + case 0xeb: + case 0xec: + case 0xed: + case 0xee: + case 0xef: + case 0xf0: + case 0xf1: + case 0xf2: + case 0xf3: + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + case 0xf8: + case 0xf9: + case 0xfa: + case 0xfb: + case 0xfc: + case 0xfd: + case 0xfe: + case 0xff: + { + return static_cast(current); + } + + default: // anything else + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 112, chars_read, + "error reading MessagePack; last byte: 0x" + ss.str())); + } + } + } + /*! + @brief determine system byte order - //////////////// - // exceptions // - //////////////// + @return true iff system's byte order is little endian - /// @name exceptions - /// Classes to implement user-defined exceptions. - /// @{ + @note from http://stackoverflow.com/a/1001328/266378 + */ + static bool little_endianess() noexcept + { + int num = 1; + return (*reinterpret_cast(&num) == 1); + } - /// @copydoc detail::exception - using exception = detail::exception; - /// @copydoc detail::parse_error - using parse_error = detail::parse_error; - /// @copydoc detail::invalid_iterator - using invalid_iterator = detail::invalid_iterator; - /// @copydoc detail::type_error - using type_error = detail::type_error; - /// @copydoc detail::out_of_range - using out_of_range = detail::out_of_range; - /// @copydoc detail::other_error - using other_error = detail::other_error; + private: + /*! + @brief get next character from the input - /// @} + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns + `std::char_traits::eof()` in that case. + @return character read from the input + */ + int get() + { + ++chars_read; + return (current = ia->get_character()); + } - ///////////////////// - // container types // - ///////////////////// + /* + @brief read a number from the input - /// @name container types - /// The canonic container types to use @ref basic_json like any other STL - /// container. - /// @{ + @tparam NumberType the type of the number - /// the type of elements in a basic_json container - using value_type = basic_json; + @return number of type @a NumberType - /// the type of an element reference - using reference = value_type&; - /// the type of an element const reference - using const_reference = const value_type&; + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. - /// 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; + @throw parse_error.110 if input has less than `sizeof(NumberType)` + bytes + */ + template NumberType get_number() + { + // step 1: read input into array with system's byte order + std::array vec; + for (size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + check_eof(); - /// the allocator type - using allocator_type = AllocatorType; + // reverse byte order prior to conversion if necessary + if (is_little_endian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } - /// the type of an element pointer - using pointer = typename std::allocator_traits::pointer; - /// the type of an element const pointer - using const_pointer = typename std::allocator_traits::const_pointer; + // step 2: convert array into number of type T and return + NumberType result; + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return result; + } - /// an iterator for a basic_json container - using iterator = iter_impl; - /// a const iterator for a basic_json container - using const_iterator = iter_impl; - /// a reverse iterator for a basic_json container - using reverse_iterator = json_reverse_iterator; - /// a const reverse iterator for a basic_json container - using const_reverse_iterator = json_reverse_iterator; + /*! + @brief create a string by reading characters from the input - /// @} + @param[in] len number of bytes to read + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref check_eof() detects the end of + the input before we run out of string memory. - /*! - @brief returns the allocator associated with the container + @return string created by reading @a len bytes + + @throw parse_error.110 if input has less than @a len bytes */ - static allocator_type get_allocator() + std::string get_string(const size_t len) { - return allocator_type(); + std::string result; + for (size_t i = 0; i < len; ++i) + { + get(); + check_eof(); + result.append(1, static_cast(current)); + } + return result; } /*! - @brief returns version information on the library - - This function returns a JSON object with information about the library, - including the version number and information on the platform and compiler. - - @return JSON object holding version information - key | description - ----------- | --------------- - `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). - `copyright` | The copyright line for the library as string. - `name` | The name of the library as string. - `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. - `url` | The URL of the project as string. - `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + @brief reads a CBOR string - @liveexample{The following code shows an example output of the `meta()` - function.,meta} + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. - @complexity Constant. + @return string - @since 2.1.0 + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read */ - static basic_json meta() + std::string get_cbor_string() { - basic_json result; + check_eof(); - result["copyright"] = "(C) 2013-2017 Niels Lohmann"; - result["name"] = "JSON for Modern C++"; - result["url"] = "https://github.com/nlohmann/json"; - result["version"] = + switch (current) { - {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1} - }; - -#ifdef _WIN32 - result["platform"] = "win32"; -#elif defined __linux__ - result["platform"] = "linux"; -#elif defined __APPLE__ - result["platform"] = "apple"; -#elif defined __unix__ - result["platform"] = "unix"; -#else - result["platform"] = "unknown"; -#endif + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6a: + case 0x6b: + case 0x6c: + case 0x6d: + case 0x6e: + case 0x6f: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + const auto len = static_cast(current & 0x1f); + return get_string(len); + } -#if defined(__clang__) - result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; -#elif defined(__ICC) || defined(__INTEL_COMPILER) - result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; -#elif defined(__GNUC__) || defined(__GNUG__) - result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; -#elif defined(__HP_cc) || defined(__HP_aCC) - result["compiler"] = "hp" -#elif defined(__IBMCPP__) - result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; -#elif defined(_MSC_VER) - result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; -#elif defined(__PGI) - result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; -#elif defined(__SUNPRO_CC) - result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; -#else - result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; -#endif + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + const auto len = static_cast(get_number()); + return get_string(len); + } -#ifdef __cplusplus - result["compiler"]["c++"] = std::to_string(__cplusplus); -#else - result["compiler"]["c++"] = "unknown"; -#endif - return result; - } + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } + case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - /////////////////////////// - // JSON value data types // - /////////////////////////// + case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) + { + const auto len = static_cast(get_number()); + return get_string(len); + } - /// @name JSON value data types - /// The data types to store a JSON value. These types are derived from - /// the template arguments passed to class @ref basic_json. - /// @{ - - /*! - @brief a type for an object - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: - > An object is an unordered collection of zero or more name/value pairs, - > where a name is a string and a value is a string, number, boolean, null, - > object, or array. - - To store objects in C++, a type is defined by the template parameters - described below. - - @tparam ObjectType the container to store objects (e.g., `std::map` or - `std::unordered_map`) - @tparam StringType the type of the keys or names (e.g., `std::string`). - The comparison function `std::less` is used to order elements - inside the container. - @tparam AllocatorType the allocator to use for objects (e.g., - `std::allocator`) + case 0x7f: // UTF-8 string (indefinite length) + { + std::string result; + while (get() != 0xff) + { + check_eof(); + result.append(1, static_cast(current)); + } + return result; + } - #### Default type + default: + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); + } + } + } - With the default values for @a ObjectType (`std::map`), @a StringType - (`std::string`), and @a AllocatorType (`std::allocator`), the default - value for @a object_t is: + /*! + @brief reads a MessagePack string - @code {.cpp} - std::map< - std::string, // key_type - basic_json, // value_type - std::less, // key_compare - std::allocator> // allocator_type - > - @endcode + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. - #### Behavior + @return string - The choice of @a object_t influences the behavior of the JSON class. With - the default type, objects have the following behavior: + @throw parse_error.110 if input ended + @throw parse_error.113 if an unexpected byte is read + */ + std::string get_msgpack_string() + { + check_eof(); - - When all names are unique, objects will be interoperable in the sense - that all software implementations receiving that object will agree on - the name-value mappings. - - When the names within an object are not unique, later stored name/value - pairs overwrite previously stored name/value pairs, leaving the used - names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will - be treated as equal and both stored as `{"key": 1}`. - - Internally, name/value pairs are stored in lexicographical order of the - names. Objects will also be serialized (see @ref dump) in this order. - For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored - and serialized as `{"a": 2, "b": 1}`. - - When comparing objects, the order of the name/value pairs is irrelevant. - This makes objects interoperable in the sense that they will not be - affected by these differences. For instance, `{"b": 1, "a": 2}` and - `{"a": 2, "b": 1}` will be treated as equal. + switch (current) + { + // fixstr + case 0xa0: + case 0xa1: + case 0xa2: + case 0xa3: + case 0xa4: + case 0xa5: + case 0xa6: + case 0xa7: + case 0xa8: + case 0xa9: + case 0xaa: + case 0xab: + case 0xac: + case 0xad: + case 0xae: + case 0xaf: + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + { + const auto len = static_cast(current & 0x1f); + return get_string(len); + } + + case 0xd9: // str 8 + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0xda: // str 16 + { + const auto len = static_cast(get_number()); + return get_string(len); + } + + case 0xdb: // str 32 + { + const auto len = static_cast(get_number()); + return get_string(len); + } - #### Limits + default: + { + std::stringstream ss; + ss << std::setw(2) << std::setfill('0') << std::hex << current; + JSON_THROW(parse_error::create( + 113, chars_read, + "expected a MessagePack string; last byte: 0x" + ss.str())); + } + } + } - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. + /*! + @brief check if input ended + @throw parse_error.110 if input ended + */ + void check_eof() const + { + if (JSON_UNLIKELY(current == std::char_traits::eof())) + { + JSON_THROW( + parse_error::create(110, chars_read, "unexpected end of input")); + } + } - In this class, the object's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON object. + private: + /// input adapter + input_adapter_t ia = nullptr; - #### Storage + /// the current character + int current = std::char_traits::eof(); - Objects are stored as pointers in a @ref basic_json type. That is, for any - access to object values, a pointer of type `object_t*` must be - dereferenced. + /// the number of characters read + size_t chars_read = 0; - @sa @ref array_t -- type for an array value + /// whether we can assume little endianess + const bool is_little_endian = true; +}; - @since version 1.0.0 +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + public: + /*! + @brief create a binary writer - @note The order name/value pairs are added to the object is *not* - preserved by the library. Therefore, iterating an object may return - name/value pairs in a different order than they were originally stored. In - fact, keys will be traversed in alphabetical order as `std::map` with - `std::less` is used by default. Please note this behavior conforms to [RFC - 7159](http://rfc7159.net/rfc7159), because any order implements the - specified "unordered" nature of JSON objects. + @param[in] adapter output adapter to write to */ - using object_t = ObjectType, - AllocatorType>>; + explicit binary_writer(output_adapter_t adapter) + : is_little_endian(binary_reader::little_endianess()), oa(adapter) + { + assert(oa); + } /*! - @brief a type for an array - - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: - > An array is an ordered sequence of zero or more values. - - To store objects in C++, a type is defined by the template parameters - explained below. - - @tparam ArrayType container type to store arrays (e.g., `std::vector` or - `std::list`) - @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - - #### Default type + @brief[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(0xf6); + break; + } - With the default values for @a ArrayType (`std::vector`) and @a - AllocatorType (`std::allocator`), the default value for @a array_t is: + case value_t::boolean: + { + oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); + break; + } - @code {.cpp} - std::vector< - basic_json, // value_type - std::allocator // allocator_type - > - @endcode + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x38); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x39); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(0x3a); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(0x3b); + write_number(static_cast(positive_number)); + } + } + break; + } - #### Limits + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x18); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x19); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + oa->write_character(0x1a); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(0x1b); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the maximum depth of nesting. + case value_t::number_float: + { + // Double-Precision Float + oa->write_character(0xfb); + write_number(j.m_value.number_float); + break; + } - In this class, the array's limit of nesting is not constraint explicitly. - However, a maximum depth of nesting may be introduced by the compiler or - runtime environment. A theoretical limit can be queried by calling the - @ref max_size function of a JSON array. + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x78); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x79); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x7a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x7b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP - #### Storage + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } - Arrays are stored as pointers in a @ref basic_json type. That is, for any - access to array values, a pointer of type `array_t*` must be dereferenced. + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0x98); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0x99); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0x9a); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0x9b); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP - @sa @ref object_t -- type for an object value + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } - @since version 1.0.0 - */ - using array_t = ArrayType>; + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xa0 + N)); + } + else if (N <= 0xff) + { + oa->write_character(0xb8); + write_number(static_cast(N)); + } + else if (N <= 0xffff) + { + oa->write_character(0xb9); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + oa->write_character(0xba); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= 0xffffffffffffffff) + { + oa->write_character(0xbb); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + default: + { + break; + } + } + } /*! - @brief a type for a string + @brief[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + // nil + oa->write_character(0xc0); + break; + } - [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: - > A string is a sequence of zero or more Unicode characters. + case value_t::boolean: + { + // true and false + oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); + break; + } - To store objects in C++, a type is defined by the template parameter - described below. Unicode values are split by the JSON class into - byte-sized characters during deserialization. + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we + // used the code from the value_t::number_unsigned case + // here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(0xd0); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(0xd1); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(0xd2); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= + (std::numeric_limits::min)() and + j.m_value.number_integer <= + (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(0xd3); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } - @tparam StringType the container to store strings (e.g., `std::string`). - Note this container is used for keys/names in objects, see @ref object_t. + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(0xcc); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(0xcd); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(0xce); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= + (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(0xcf); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } - #### Default type + case value_t::number_float: + { + // float 64 + oa->write_character(0xcb); + write_number(j.m_value.number_float); + break; + } - With the default values for @a StringType (`std::string`), the default - value for @a string_t is: + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xa0 | N)); + } + else if (N <= 255) + { + // str 8 + oa->write_character(0xd9); + write_number(static_cast(N)); + } + else if (N <= 65535) + { + // str 16 + oa->write_character(0xda); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // str 32 + oa->write_character(0xdb); + write_number(static_cast(N)); + } - @code {.cpp} - std::string - @endcode + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } - #### Encoding + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= 0xffff) + { + // array 16 + oa->write_character(0xdc); + write_number(static_cast(N)); + } + else if (N <= 0xffffffff) + { + // array 32 + oa->write_character(0xdd); + write_number(static_cast(N)); + } - Strings are stored in UTF-8 encoding. Therefore, functions like - `std::string::size()` or `std::string::length()` return the number of - bytes in the string rather than the number of characters or glyphs. + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } - #### String comparison + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xf))); + } + else if (N <= 65535) + { + // map 16 + oa->write_character(0xde); + write_number(static_cast(N)); + } + else if (N <= 4294967295) + { + // map 32 + oa->write_character(0xdf); + write_number(static_cast(N)); + } - [RFC 7159](http://rfc7159.net/rfc7159) states: - > Software implementations are typically required to test names of object - > members for equality. Implementations that transform the textual - > representation into sequences of Unicode code units and then perform the - > comparison numerically, code unit by code unit, are interoperable in the - > sense that implementations will agree in all cases on equality or - > inequality of two strings. For example, implementations that compare - > strings with escaped characters unconverted may incorrectly find that - > `"a\\b"` and `"a\u005Cb"` are not equal. + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } - This implementation is interoperable as it does compare strings code unit - by code unit. + default: + { + break; + } + } + } - #### Storage + private: + /* + @brief write a number to output input - String values are stored as pointers in a @ref basic_json type. That is, - for any access to string values, a pointer of type `string_t*` must be - dereferenced. + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number - @since version 1.0.0 + @note This function needs to respect the system's endianess, because + bytes in CBOR and MessagePack are stored in network order (big + endian) and therefore need reordering on little endian systems. */ - using string_t = StringType; + template void write_number(NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec; + std::memcpy(vec.data(), &n, sizeof(NumberType)); - /*! - @brief a type for a boolean + // step 2: write array to output (with possible reordering) + if (is_little_endian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } - [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a - type which differentiates the two literals `true` and `false`. + oa->write_characters(vec.data(), sizeof(NumberType)); + } - To store objects in C++, a type is defined by the template parameter @a - BooleanType which chooses the type to use. + private: + /// whether we can assume little endianess + const bool is_little_endian = true; - #### Default type + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail - With the default values for @a BooleanType (`bool`), the default value for - @a boolean_t is: +/// namespace to hold default `to_json` / `from_json` functions +namespace +{ +constexpr const auto& to_json = detail::static_const::value; +constexpr const auto& from_json = detail::static_const::value; +} - @code {.cpp} - bool - @endcode - #### Storage +/*! +@brief default JSONSerializer template argument - Boolean values are stored directly inside a @ref basic_json type. +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type - @since version 1.0.0 + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @param[in] j JSON value to read from + @param[in,out] val value to write to */ - using boolean_t = BooleanType; + template + static void from_json(BasicJsonType&& j, ValueType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + { + ::nlohmann::from_json(std::forward(j), val); + } /*! - @brief a type for a number (integer) + @brief convert any value type to a JSON value - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + This function is usually called by the constructors of the @ref basic_json + class. - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static void to_json(BasicJsonType& j, ValueType&& val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; - To store integer numbers in C++, a type is defined by the template - parameter @a NumberIntegerType which chooses the type to use. - - #### Default type +/*! +@brief JSON Pointer - With the default values for @a NumberIntegerType (`int64_t`), the default - value for @a number_integer_t is: +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. - @code {.cpp} - int64_t - @endcode +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) - #### Default behavior +@since version 2.0.0 +*/ +class json_pointer +{ + /// allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + public: + /*! + @brief create JSON pointer - #### Limits + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. + @param[in] s string representing the JSON pointer; if omitted, the + empty string is assumed which references the whole JSON + value - When the default type is used, the maximal integer number that can be - stored is `9223372036854775807` (INT64_MAX) and the minimal integer number - that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers - that are out of range will yield over/underflow when used in a - constructor. During deserialization, too large or small integer numbers - will be automatically be stored as @ref number_unsigned_t or @ref - number_float_t. + @throw parse_error.107 if the given JSON pointer @a s is nonempty and + does not begin with a slash (`/`); see example below - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s + is not followed by `0` (representing `~`) or `1` (representing `/`); + see example below - As this range is a subrange of the exactly supported range [INT64_MIN, - INT64_MAX], this class's integer type is interoperable. + @liveexample{The example shows the construction several valid JSON + pointers as well as the exceptional behavior.,json_pointer} - #### Storage + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") : reference_tokens(split(s)) {} - Integer number values are stored directly inside a @ref basic_json type. + /*! + @brief return a string representation of the JSON pointer - @sa @ref number_float_t -- type for number values (floating-point) + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode - @sa @ref number_unsigned_t -- type for number values (unsigned integer) + @return a string representation of the JSON pointer - @since version 1.0.0 - */ - using number_integer_t = NumberIntegerType; + @liveexample{The example shows the result of `to_string`., + json_pointer__to_string} - /*! - @brief a type for a number (unsigned) + @since version 2.0.0 + */ + std::string to_string() const noexcept + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + escape(b); + }); + } - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + private: + /*! + @brief remove and return last reference pointer + @throw out_of_range.405 if JSON pointer has no parent + */ + std::string pop_back() + { + if (is_root()) + { + JSON_THROW( + detail::out_of_range::create(405, "JSON pointer has no parent")); + } - To store unsigned integer numbers in C++, a type is defined by the - template parameter @a NumberUnsignedType which chooses the type to use. + auto last = reference_tokens.back(); + reference_tokens.pop_back(); + return last; + } - #### Default type + /// return whether pointer points to the root document + bool is_root() const + { + return reference_tokens.empty(); + } - With the default values for @a NumberUnsignedType (`uint64_t`), the - default value for @a number_unsigned_t is: + json_pointer top() const + { + if (is_root()) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent")); + } - @code {.cpp} - uint64_t - @endcode + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } - #### Default behavior - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in integer literals lead to an interpretation as octal - number. Internally, the value will be stored as decimal number. For - instance, the C++ integer literal `010` will be serialized to `8`. - During deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + /*! + @brief create and return a reference to the pointed to value - #### Limits + @complexity Linear in the number of reference tokens. - [RFC 7159](http://rfc7159.net/rfc7159) specifies: - > An implementation may set limits on the range and precision of numbers. + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_and_create(NLOHMANN_BASIC_JSON_TPL& j) const; - When the default type is used, the maximal integer number that can be - stored is `18446744073709551615` (UINT64_MAX) and the minimal integer - number that can be stored is `0`. Integer numbers that are out of range - will yield over/underflow when used in a constructor. During - deserialization, too large or small integer numbers will be automatically - be stored as @ref number_integer_t or @ref number_float_t. + /*! + @brief return a reference to the pointed to value - [RFC 7159](http://rfc7159.net/rfc7159) further states: - > Note that when such software is used, numbers that are integers and are - > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense - > that implementations will agree exactly on their numeric values. + @note This version does not throw if a value is not present, but tries + to create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. - As this range is a subrange (when considered in conjunction with the - number_integer_t type) of the exactly supported range [0, UINT64_MAX], - this class's integer type is interoperable. + @param[in] ptr a JSON value - #### Storage + @return reference to the JSON value pointed to by the JSON pointer - Integer number values are stored directly inside a @ref basic_json type. + @complexity Linear in the length of the JSON pointer. - @sa @ref number_float_t -- type for number values (floating-point) - @sa @ref number_integer_t -- type for number values (integer) + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_unchecked(NLOHMANN_BASIC_JSON_TPL* ptr) const; - @since version 2.0.0 - */ - using number_unsigned_t = NumberUnsignedType; + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + NLOHMANN_BASIC_JSON_TPL& get_checked(NLOHMANN_BASIC_JSON_TPL* ptr) const; /*! - @brief a type for a number (floating-point) + @brief return a const reference to the pointed to value - [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: - > The representation of numbers is similar to that used in most - > programming languages. A number is represented in base 10 using decimal - > digits. It contains an integer component that may be prefixed with an - > optional minus sign, which may be followed by a fraction part and/or an - > exponent part. Leading zeros are not allowed. (...) Numeric values that - > cannot be represented in the grammar below (such as Infinity and NaN) - > are not permitted. + @param[in] ptr a JSON value - This description includes both integer and floating-point numbers. - However, C++ allows more precise storage if it is known whether the number - is a signed integer, an unsigned integer or a floating-point number. - Therefore, three different types, @ref number_integer_t, @ref - number_unsigned_t and @ref number_float_t are used. + @return const reference to the JSON value pointed to by the JSON + pointer - To store floating-point numbers in C++, a type is defined by the template - parameter @a NumberFloatType which chooses the type to use. + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + const NLOHMANN_BASIC_JSON_TPL& get_unchecked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; - #### Default type + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + const NLOHMANN_BASIC_JSON_TPL& get_checked(const NLOHMANN_BASIC_JSON_TPL* ptr) const; - With the default values for @a NumberFloatType (`double`), the default - value for @a number_float_t is: + /*! + @brief split the string input to reference tokens - @code {.cpp} - double - @endcode + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. - #### Default behavior + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; - - The restrictions about leading zeros is not enforced in C++. Instead, - leading zeros in floating-point literals will be ignored. Internally, - the value will be stored as decimal number. For instance, the C++ - floating-point literal `01.2` will be serialized to `1.2`. During - deserialization, leading zeros yield an error. - - Not-a-number (NaN) values will be serialized to `null`. + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } - #### Limits + // check if nonempty reference string begins with slash + if (reference_string[0] != '/') + { + JSON_THROW(detail::parse_error::create( + 107, 1, + "JSON pointer must be empty or begin with '/' - was: '" + + reference_string + "'")); + } - [RFC 7159](http://rfc7159.net/rfc7159) states: - > This specification allows implementations to set limits on the range and - > precision of numbers accepted. Since software that implements IEEE - > 754-2008 binary64 (double precision) numbers is generally available and - > widely used, good interoperability can be achieved by implementations - > that expect no more precision or range than these provide, in the sense - > that implementations will approximate JSON numbers within the expected - > precision. + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == string::npos+1 = 0 + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); - This implementation does exactly follow this approach, as it uses double - precision floating-point numbers. Note values smaller than - `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` - will be stored as NaN internally and be serialized to `null`. + // check reference tokens are properly escaped + for (size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + assert(reference_token[pos] == '~'); - #### Storage + // ~ must be followed by 0 or 1 + if (pos == reference_token.size() - 1 or + (reference_token[pos + 1] != '0' and + reference_token[pos + 1] != '1')) + { + JSON_THROW(detail::parse_error::create( + 108, 0, "escape character '~' must be followed with '0' or '1'")); + } + } - Floating-point number values are stored directly inside a @ref basic_json - type. + // finally, store the reference token + unescape(reference_token); + result.push_back(reference_token); + } - @sa @ref number_integer_t -- type for number values (integer) + return result; + } - @sa @ref number_unsigned_t -- type for number values (unsigned integer) + /*! + @brief replace all occurrences of a substring by another string - @since version 1.0.0 - */ - using number_float_t = NumberFloatType; + @param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t + @param[in] f the substring to replace with @a t + @param[in] t the string to replace @a f - /// @} + @pre The search string @a f must not be empty. **This precondition is + enforced with an assertion.** - private: + @since version 2.0.0 + */ + static void replace_substring(std::string& s, const std::string& f, + const std::string& t) + { + assert(not f.empty()); - /// helper for exception-safe object creation - template - static T* create(Args&& ... args) + for (size_t pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t + pos = s.find(f, pos + t.size()) // find next occurrence of f + ) + ; + } + + /// escape tilde and slash + static std::string escape(std::string s) { - AllocatorType alloc; - auto deleter = [&](T * object) - { - alloc.deallocate(object, 1); - }; - std::unique_ptr object(alloc.allocate(1), deleter); - alloc.construct(object.get(), std::forward(args)...); - assert(object != nullptr); - return object.release(); + // escape "~"" to "~0" and "/" to "~1" + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; } - //////////////////////// - // JSON value storage // - //////////////////////// + /// unescape tilde and slash + static void unescape(std::string& s) + { + // first transform any occurrence of the sequence '~1' to '/' + replace_substring(s, "~1", "/"); + // then transform any occurrence of the sequence '~0' to '~' + replace_substring(s, "~0", "~"); + } /*! - @brief a JSON value - - The actual storage for a JSON value of the @ref basic_json class. This - union combines the different storage types for the JSON value types - defined in @ref value_t. - - JSON type | value_t type | used type - --------- | --------------- | ------------------------ - object | object | pointer to @ref object_t - array | array | pointer to @ref array_t - string | string | pointer to @ref string_t - boolean | boolean | @ref boolean_t - number | number_integer | @ref number_integer_t - number | number_unsigned | @ref number_unsigned_t - number | number_float | @ref number_float_t - null | null | *no value is stored* - - @note Variable-length types (objects, arrays, and strings) are stored as - pointers. The size of the union should not exceed 64 bits if the default - value types are used. + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to - @since version 1.0.0 - */ - 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; - /// boolean - boolean_t boolean; - /// number (integer) - number_integer_t number_integer; - /// number (unsigned integer) - number_unsigned_t number_unsigned; - /// number (floating-point) - number_float_t number_float; + @note Empty objects or arrays are flattened to `null`. + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + static void flatten(const std::string& reference_string, + const NLOHMANN_BASIC_JSON_TPL& value, + NLOHMANN_BASIC_JSON_TPL& result); - /// default constructor (for null values) - json_value() = default; - /// constructor for booleans - json_value(boolean_t v) noexcept : boolean(v) {} - /// constructor for numbers (integer) - json_value(number_integer_t v) noexcept : number_integer(v) {} - /// constructor for numbers (unsigned) - json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} - /// constructor for numbers (floating-point) - json_value(number_float_t v) noexcept : number_float(v) {} - /// constructor for empty values of a given type - json_value(value_t t) - { - switch (t) - { - case value_t::object: - { - object = create(); - break; - } + /*! + @param[in] value flattened JSON - case value_t::array: - { - array = create(); - break; - } + @return unflattened JSON - case value_t::string: - { - string = create(""); - break; - } + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + NLOHMANN_BASIC_JSON_TPL_DECLARATION + static NLOHMANN_BASIC_JSON_TPL + unflatten(const NLOHMANN_BASIC_JSON_TPL& value); - case value_t::boolean: - { - boolean = boolean_t(false); - break; - } + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept; - case value_t::number_integer: - { - number_integer = number_integer_t(0); - break; - } + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept; - case value_t::number_unsigned: - { - number_unsigned = number_unsigned_t(0); - break; - } + /// the reference tokens + std::vector reference_tokens; +}; - case value_t::number_float: - { - number_float = number_float_t(0.0); - break; - } +/*! +@brief a class to store JSON values - case value_t::null: - { - break; - } +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) - default: - { - if (JSON_UNLIKELY(t == value_t::null)) - { - JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE - } - break; - } - } - } +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](http://en.cppreference.com/w/cpp/concept/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](http://en.cppreference.com/w/cpp/concept/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](http://en.cppreference.com/w/cpp/concept/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](http://en.cppreference.com/w/cpp/concept/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](http://en.cppreference.com/w/cpp/concept/StandardLayoutType): + JSON values have + [standard layout](http://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](http://en.cppreference.com/w/cpp/concept/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](http://en.cppreference.com/w/cpp/concept/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](http://en.cppreference.com/w/cpp/concept/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](http://en.cppreference.com/w/cpp/concept/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](http://en.cppreference.com/w/cpp/concept/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. - /// constructor for strings - json_value(const string_t& value) - { - string = create(value); - } +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). - /// constructor for objects - json_value(const object_t& value) - { - object = create(value); - } +@internal +@note ObjectType trick from http://stackoverflow.com/a/9860911 +@endinternal - /// constructor for arrays - json_value(const array_t& value) - { - array = create(value); - } - }; +@see [RFC 7159: The JavaScript Object Notation (JSON) Data Interchange +Format](http://rfc7159.net/rfc7159) - /*! - @brief checks the class invariants +@since version 1.0.0 - This function asserts the class invariants. It needs to be called at the - end of every constructor to make sure that created objects respect the - invariant. Furthermore, it has to be called each time the type of a JSON - value is changed, because the invariant expresses a relationship between - @a m_type and @a m_value. - */ - void assert_invariant() const - { - assert(m_type != value_t::object or m_value.object != nullptr); - assert(m_type != value_t::array or m_value.array != nullptr); - assert(m_type != value_t::string or m_value.string != nullptr); - } +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::detail::parser; + template + friend class ::nlohmann::detail::iter_impl; + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; - public: - ////////////////////////// - // JSON parser callback // - ////////////////////////// + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer; + using parser = ::nlohmann::detail::parser; - using parse_event_t = typename parser::parse_event_t; + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; - /*! - @brief per-element parser callback type + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; - With a parser callback function, the result of parsing a JSON text can be - influenced. When passed to @ref parse(std::istream&, const - parser_callback_t) or @ref parse(const CharT, const parser_callback_t), - it is called on certain events (passed as @ref parse_event_t via parameter - @a event) with a set recursion depth @a depth and context JSON value - @a parsed. The return value of the callback function is a boolean - indicating whether the element that emitted the callback shall be kept or - not. + using binary_reader = ::nlohmann::detail::binary_reader; + using binary_writer = ::nlohmann::detail::binary_writer; - We distinguish six scenarios (determined by the event type) in which the - callback function can be called. The following table describes the values - of the parameters @a depth, @a event, and @a parsed. + public: + using value_t = detail::value_t; + // forward declarations + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; - parameter @a event | description | parameter @a depth | parameter @a parsed - ------------------ | ----------- | ------------------ | ------------------- - parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded - parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key - parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object - parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded - parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array - parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - @image html callback_events.png "Example when certain parse events are triggered" + //////////////// + // exceptions // + //////////////// - Discarding a value (i.e., returning `false`) has different effects - depending on the context in which function was called: + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ - - Discarded values in structured types are skipped. That is, the parser - will behave as if the discarded value was never read. - - In case a value outside a structured type is skipped, it is replaced - with `null`. This case happens if the top-level element is skipped. + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; - @param[in] depth the depth of the recursion during parsing + /// @} - @param[in] event an event of type parse_event_t indicating the context in - the callback function has been called - @param[in,out] parsed the current intermediate parse result; note that - writing to this value has no effect for parse_event_t::key events + ///////////////////// + // container types // + ///////////////////// - @return Whether the JSON value which called the function during parsing - should be kept (`true`) or not (`false`). In the latter case, it is either - skipped completely or replaced by an empty discarded object. + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ - @sa @ref parse(std::istream&, parser_callback_t) or - @ref parse(const CharT, const parser_callback_t) for examples + /// the type of elements in a basic_json container + using value_type = basic_json; - @since version 1.0.0 - */ - using parser_callback_t = typename parser::parser_callback_t; + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + /// 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; - ////////////////// - // constructors // - ////////////////// + /// the allocator type + using allocator_type = AllocatorType; - /// @name constructors and destructors - /// Constructors of class @ref basic_json, copy/move constructor, copy - /// assignment, static functions creating objects, and the destructor. - /// @{ + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; - /*! - @brief create an empty value with a given type + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; - Create an empty JSON value with a given type. The value will be default - initialized with an empty value which depends on the type: + /// @} - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` - - @param[in] v the type of the value to create - - @complexity Constant. - - @liveexample{The following code shows the constructor for different @ref - value_t values,basic_json__value_t} - @since version 1.0.0 + /*! + @brief returns the allocator associated with the container */ - basic_json(const value_t v) - : m_type(v), m_value(v) + static allocator_type get_allocator() { - assert_invariant(); + return allocator_type(); } /*! - @brief create a null object + @brief returns version information on the library - Create a `null` JSON value. It either takes a null pointer as parameter - (explicitly creating `null`) or no parameter (implicitly creating `null`). - The passed null pointer itself is not read -- it is only used to choose - the right constructor. + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. - @complexity Constant. + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). - @exceptionsafety No-throw guarantee: this constructor never throws - exceptions. + @liveexample{The following code shows an example output of the `meta()` + function.,meta} - @liveexample{The following code shows the constructor with and without a - null pointer parameter.,basic_json__nullptr_t} + @complexity Constant. - @since version 1.0.0 + @since 2.1.0 */ - basic_json(std::nullptr_t = nullptr) noexcept - : basic_json(value_t::null) + static basic_json meta() { - assert_invariant(); - } + basic_json result; - /*! - @brief create a JSON value + result["copyright"] = "(C) 2013-2017 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"] = + { + {"string", "2.1.1"}, {"major", 2}, {"minor", 1}, {"patch", 1} + }; - This is a "catch all" constructor for all compatible JSON types; that is, - types for which a `to_json()` method exsits. The constructor forwards the - parameter @a val to that method (to `json_serializer::to_json` method - with `U = uncvref_t`, to be exact). +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif - Template type @a CompatibleType includes, but is not limited to, the - following types: - - **arrays**: @ref array_t and all kinds of compatible containers such as - `std::vector`, `std::deque`, `std::list`, `std::forward_list`, - `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and - `unordered_multiset` with a `value_type` from which a @ref basic_json - value can be constructed. - - **objects**: @ref object_t and all kinds of compatible associative - containers such as `std::map`, `std::unordered_map`, `std::multimap`, - and `std::unordered_multimap` with a `key_type` compatible to - @ref string_t and a `value_type` from which a @ref basic_json value can - be constructed. - - **strings**: @ref string_t, string literals, and all compatible string - containers can be used. - - **numbers**: @ref number_integer_t, @ref number_unsigned_t, - @ref number_float_t, and all convertible number types such as `int`, - `size_t`, `int64_t`, `float` or `double` can be used. - - **boolean**: @ref boolean_t / `bool` can be used. +#if defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif - See the examples below. +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } - @tparam CompatibleType a type such that: - - @a CompatibleType is not derived from `std::istream`, - - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move - constructors), - - @a CompatibleType is not a @ref basic_json nested type (e.g., - @ref json_pointer, @ref iterator, etc ...) - - @ref @ref json_serializer has a - `to_json(basic_json_t&, CompatibleType&&)` method - @tparam U = `uncvref_t` + /////////////////////////// + // JSON value data types // + /////////////////////////// - @param[in] val the value to be forwarded + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ - @complexity Usually linear in the size of the passed @a val, also - depending on the implementation of the called `to_json()` - method. + /*! + @brief a type for an object - @throw what `json_serializer::to_json()` throws + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. - @liveexample{The following code shows the constructor with several - compatible types.,basic_json__CompatibleType} + To store objects in C++, a type is defined by the template parameters + described below. - @since version 2.1.0 - */ - template, - detail::enable_if_t::value and - not std::is_same::value and - not detail::is_basic_json_nested_type< - basic_json_t, U>::value and - detail::has_to_json::value, - int> = 0> - basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( - std::declval(), std::forward(val)))) - { - JSONSerializer::to_json(*this, std::forward(val)); - assert_invariant(); - } + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) - /*! - @brief create a container (array or object) from an initializer list + #### Default type - Creates a JSON value of type array or object from the passed initializer - list @a init. In case @a type_deduction is `true` (default), the type of - the JSON value to be created is deducted from the initializer list @a init - according to the following rules: + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: - 1. If the list is empty, an empty JSON object value `{}` is created. - 2. If the list consists of pairs whose first element is a string, a JSON - object value is created where the first elements of the pairs are - treated as keys and the second elements are as values. - 3. In all other cases, an array is created. + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode - The rules aim to create the best fit between a C++ initializer list and - JSON values. The rationale is as follows: + #### Behavior - 1. The empty initializer list is written as `{}` which is exactly an empty - JSON object. - 2. C++ has now way of describing mapped types other than to list a list of - pairs. As JSON requires that keys must be of type string, rule 2 is the - weakest constraint one can pose on initializer lists to interpret them - as an object. - 3. In all other cases, the initializer list could not be interpreted as - JSON object type, so interpreting it as JSON array type is safe. + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: - With the rules described above, the following JSON values cannot be - expressed by an initializer list: + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, later stored name/value + pairs overwrite previously stored name/value pairs, leaving the used + names unique. For instance, `{"key": 1}` and `{"key": 2, "key": 1}` will + be treated as equal and both stored as `{"key": 1}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. - - the empty array (`[]`): use @ref array(std::initializer_list) - with an empty initializer list in this case - - arrays whose elements satisfy rule 2: use @ref - array(std::initializer_list) with the same initializer list - in this case + #### Limits - @note When used without parentheses around an empty initializer list, @ref - basic_json() is called instead of this function, yielding the JSON null - value. + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. - @param[in] init initializer list with JSON values + In this class, the object's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. - @param[in] type_deduction internal parameter; when set to `true`, the type - of the JSON value is deducted from the initializer list @a init; when set - to `false`, the type provided via @a manual_type is forced. This mode is - used by the functions @ref array(std::initializer_list) and - @ref object(std::initializer_list). + #### Storage - @param[in] manual_type internal parameter; when @a type_deduction is set - to `false`, the created JSON value will use the provided type (only @ref - value_t::array and @ref value_t::object are valid); when @a type_deduction - is set to `true`, this parameter has no effect + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. - @throw type_error.301 if @a type_deduction is `false`, @a manual_type is - `value_t::object`, but @a init contains an element which is not a pair - whose first element is a string. In this case, the constructor could not - create an object. If @a type_deduction would have be `true`, an array - would have been created. See @ref object(std::initializer_list) - for an example. - - @complexity Linear in the size of the initializer list @a init. - - @liveexample{The example below shows how JSON values are created from - initializer lists.,basic_json__list_init_t} - - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list + @sa @ref array_t -- type for an array value @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 7159](http://rfc7159.net/rfc7159), because any order implements the + specified "unordered" nature of JSON objects. */ - basic_json(std::initializer_list init, - bool type_deduction = true, - value_t manual_type = value_t::array) - { - // check if each element is an array with two elements whose first - // element is a string - bool is_an_object = std::all_of(init.begin(), init.end(), - [](const basic_json & element) - { - return element.is_array() and element.size() == 2 and element[0].is_string(); - }); + using object_t = ObjectType, + AllocatorType>>; - // adjust type if type deduction is not wanted - if (not type_deduction) - { - // if array is wanted, do not create an object though possible - if (manual_type == value_t::array) - { - is_an_object = false; - } + /*! + @brief a type for an array - // if object is wanted but impossible, throw an exception - if (manual_type == value_t::object and not is_an_object) - { - JSON_THROW(type_error::create(301, "cannot create object from initializer list")); - } - } + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. - if (is_an_object) - { - // the initializer list is a list of pairs -> create object - m_type = value_t::object; - m_value = value_t::object; + To store objects in C++, a type is defined by the template parameters + explained below. - std::for_each(init.begin(), init.end(), [this](const basic_json & element) - { - m_value.object->emplace(*(element[0].m_value.string), element[1]); - }); - } - else - { - // the initializer list describes an array -> create array - m_type = value_t::array; - m_value.array = create(init); - } + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) - assert_invariant(); - } + #### Default type - /*! - @brief explicitly create an array from an initializer list + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: - Creates a JSON array value from a given initializer list. That is, given a - list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the - initializer list is empty, the empty array `[]` is created. + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode - @note This function is only needed to express two edge cases that cannot - be realized with the initializer list constructor (@ref - basic_json(std::initializer_list, bool, value_t)). These cases - are: - 1. creating an array whose elements are all pairs whose first element is a - string -- in this case, the initializer list constructor would create an - object, taking the first elements as keys - 2. creating an empty array -- passing the empty initializer list to the - initializer list constructor yields an empty object + #### Limits - @param[in] init initializer list with JSON values to create an array from - (optional) + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the maximum depth of nesting. - @return JSON array value + In this class, the array's limit of nesting is not constraint explicitly. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. - @complexity Linear in the size of @a init. + #### Storage - @liveexample{The following code shows an example for the `array` - function.,array} + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref object(std::initializer_list) -- create a JSON object - value from an initializer list + @sa @ref object_t -- type for an object value @since version 1.0.0 */ - static basic_json array(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::array); - } + using array_t = ArrayType>; /*! - @brief explicitly create an object from an initializer list - - Creates a JSON object value from a given initializer list. The initializer - lists elements must be pairs, and their first elements must be strings. If - the initializer list is empty, the empty object `{}` is created. + @brief a type for a string - @note This function is only added for symmetry reasons. In contrast to the - related function @ref array(std::initializer_list), there are - no cases which can only be expressed by this function. That is, any - initializer list @a init can also be passed to the initializer list - constructor @ref basic_json(std::initializer_list, bool, value_t). + [RFC 7159](http://rfc7159.net/rfc7159) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. - @param[in] init initializer list to create an object from (optional) + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. - @return JSON object value + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. - @throw type_error.301 if @a init is not a list of pairs whose first - elements are strings. In this case, no object can be created. When such a - value is passed to @ref basic_json(std::initializer_list, bool, value_t), - an array would have been created from the passed initializer list @a init. - See example below. + #### Default type - @complexity Linear in the size of @a init. + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: - @liveexample{The following code shows an example for the `object` - function.,object} + @code {.cpp} + std::string + @endcode - @sa @ref basic_json(std::initializer_list, bool, value_t) -- - create a JSON value from an initializer list - @sa @ref array(std::initializer_list) -- create a JSON array - value from an initializer list + #### Encoding - @since version 1.0.0 - */ - static basic_json object(std::initializer_list init = - std::initializer_list()) - { - return basic_json(init, false, value_t::object); - } + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. - /*! - @brief construct an array with count copies of given value + #### String comparison - Constructs a JSON array value by creating @a cnt copies of a passed value. - In case @a cnt is `0`, an empty array is created. As postcondition, - `std::distance(begin(),end()) == cnt` holds. + [RFC 7159](http://rfc7159.net/rfc7159) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. - @param[in] cnt the number of JSON copies of @a val to create - @param[in] val the JSON value to copy + This implementation is interoperable as it does compare strings code unit + by code unit. - @complexity Linear in @a cnt. + #### Storage - @liveexample{The following code shows examples for the @ref - basic_json(size_type\, const basic_json&) - constructor.,basic_json__size_type_basic_json} + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. @since version 1.0.0 */ - basic_json(size_type cnt, const basic_json& val) - : m_type(value_t::array) - { - m_value.array = create(cnt, val); - assert_invariant(); - } + using string_t = StringType; /*! - @brief construct a JSON container given an iterator range + @brief a type for a boolean - Constructs the JSON value with the contents of the range `[first, last)`. - The semantics depends on the different types a JSON value can have: - - In case of primitive types (number, boolean, or string), @a first must - be `begin()` and @a last must be `end()`. In this case, the value is - copied. Otherwise, invalid_iterator.204 is thrown. - - In case of structured types (array, object), the constructor behaves as - similar versions for `std::vector`. - - In case of a null type, invalid_iterator.206 is thrown. + [RFC 7159](http://rfc7159.net/rfc7159) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. - @tparam InputIT an input iterator type (@ref iterator or @ref - const_iterator) + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. - @param[in] first begin of the range to copy from (included) - @param[in] last end of the range to copy from (excluded) + #### Default type - @pre Iterators @a first and @a last must be initialized. **This - precondition is enforced with an assertion.** + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: - @pre Range `[first, last)` is valid. Usually, this precondition cannot be - checked efficiently. Only certain edge cases are detected; see the - description of the exceptions below. + @code {.cpp} + bool + @endcode - @throw invalid_iterator.201 if iterators @a first and @a last are not - compatible (i.e., do not belong to the same JSON value). In this case, - the range `[first, last)` is undefined. - @throw invalid_iterator.204 if iterators @a first and @a last belong to a - primitive type (number, boolean, or string), but @a first does not point - to the first element any more. In this case, the range `[first, last)` is - undefined. See example code below. - @throw invalid_iterator.206 if iterators @a first and @a last belong to a - null value. In this case, the range `[first, last)` is undefined. + #### Storage - @complexity Linear in distance between @a first and @a last. - - @liveexample{The example below shows several ways to create JSON values by - specifying a subrange with iterators.,basic_json__InputIt_InputIt} + Boolean values are stored directly inside a @ref basic_json type. @since version 1.0.0 */ - template::value or - std::is_same::value, int>::type = 0> - basic_json(InputIT first, InputIT last) - { - assert(first.m_object != nullptr); - assert(last.m_object != nullptr); - - // make sure iterator fits the current value - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); - } - - // copy type from first iterator - m_type = first.m_object->m_type; - - // check if iterator range is complete for primitive values - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } - break; - } + using boolean_t = BooleanType; - default: - { - break; - } - } + /*! + @brief a type for a number (integer) - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = first.m_object->m_value.number_integer; - break; - } + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - case value_t::number_unsigned: - { - m_value.number_unsigned = first.m_object->m_value.number_unsigned; - break; - } + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - case value_t::number_float: - { - m_value.number_float = first.m_object->m_value.number_float; - break; - } + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. - case value_t::boolean: - { - m_value.boolean = first.m_object->m_value.boolean; - break; - } + #### Default type - case value_t::string: - { - m_value = *first.m_object->m_value.string; - break; - } + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: - case value_t::object: - { - m_value.object = create(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } + @code {.cpp} + int64_t + @endcode - case value_t::array: - { - m_value.array = create(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } + #### Default behavior - default: - { - JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + - first.m_object->type_name())); - } - } + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - assert_invariant(); - } + #### Limits + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. - /////////////////////////////////////// - // other constructors and destructor // - /////////////////////////////////////// + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. - /*! - @brief copy constructor + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. - Creates a copy of a given JSON value. + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. - @param[in] other the JSON value to copy + #### Storage - @complexity Linear in the size of @a other. + Integer number values are stored directly inside a @ref basic_json type. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - As postcondition, it holds: `other == basic_json(other)`. + @sa @ref number_float_t -- type for number values (floating-point) - @liveexample{The following code shows an example for the copy - constructor.,basic_json__basic_json} + @sa @ref number_unsigned_t -- type for number values (unsigned integer) @since version 1.0.0 */ - basic_json(const basic_json& other) - : m_type(other.m_type) - { - // check of passed value is valid - other.assert_invariant(); + using number_integer_t = NumberIntegerType; - switch (m_type) - { - case value_t::object: - { - m_value = *other.m_value.object; - break; - } + /*! + @brief a type for a number (unsigned) - case value_t::array: - { - m_value = *other.m_value.array; - break; - } + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - case value_t::string: - { - m_value = *other.m_value.string; - break; - } + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - case value_t::boolean: - { - m_value = other.m_value.boolean; - break; - } + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. - case value_t::number_integer: - { - m_value = other.m_value.number_integer; - break; - } + #### Default type - case value_t::number_unsigned: - { - m_value = other.m_value.number_unsigned; - break; - } + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: - case value_t::number_float: - { - m_value = other.m_value.number_float; - break; - } + @code {.cpp} + uint64_t + @endcode - default: - { - break; - } - } + #### Default behavior - assert_invariant(); - } + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - /*! - @brief move constructor + #### Limits - Move constructor. Constructs a JSON value with the contents of the given - value @a other using move semantics. It "steals" the resources from @a - other and leaves it as JSON null value. + [RFC 7159](http://rfc7159.net/rfc7159) specifies: + > An implementation may set limits on the range and precision of numbers. - @param[in,out] other value to move to this object + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. - @post @a other is a JSON null value + [RFC 7159](http://rfc7159.net/rfc7159) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. - @complexity Constant. + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. - @liveexample{The code below shows the move constructor explicitly called - via std::move.,basic_json__moveconstructor} - - @since version 1.0.0 - */ - basic_json(basic_json&& other) noexcept - : m_type(std::move(other.m_type)), - m_value(std::move(other.m_value)) - { - // check that passed value is valid - other.assert_invariant(); - - // invalidate payload - other.m_type = value_t::null; - other.m_value = {}; - - assert_invariant(); - } - - /*! - @brief copy assignment - - Copy assignment operator. Copies a JSON value via the "copy and swap" - strategy: It is expressed in terms of the copy constructor, destructor, - and the swap() member function. - - @param[in] other value to copy from - - @complexity Linear. + #### Storage - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. + Integer number values are stored directly inside a @ref basic_json type. - @liveexample{The code below shows and example for the copy assignment. It - creates a copy of value `a` which is then swapped with `b`. Finally\, the - copy of `a` (which is the null value after the swap) is - destroyed.,basic_json__copyassignment} + @sa @ref number_float_t -- type for number values (floating-point) + @sa @ref number_integer_t -- type for number values (integer) - @since version 1.0.0 + @since version 2.0.0 */ - reference& operator=(basic_json other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) - { - // check that passed value is valid - other.assert_invariant(); - - using std::swap; - swap(m_type, other.m_type); - swap(m_value, other.m_value); - - assert_invariant(); - return *this; - } + using number_unsigned_t = NumberUnsignedType; /*! - @brief destructor + @brief a type for a number (floating-point) - Destroys the JSON value and frees all allocated memory. + [RFC 7159](http://rfc7159.net/rfc7159) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. - @complexity Linear. + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is linear. - - All stored elements are destroyed and all memory is freed. + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. - @since version 1.0.0 - */ - ~basic_json() - { - assert_invariant(); + #### Default type - switch (m_type) - { - case value_t::object: - { - AllocatorType alloc; - alloc.destroy(m_value.object); - alloc.deallocate(m_value.object, 1); - break; - } + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: - case value_t::array: - { - AllocatorType alloc; - alloc.destroy(m_value.array); - alloc.deallocate(m_value.array, 1); - break; - } + @code {.cpp} + double + @endcode - case value_t::string: - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - break; - } + #### Default behavior - default: - { - // all other types need no specific destructor - break; - } - } - } + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. - /// @} + #### Limits - public: - /////////////////////// - // object inspection // - /////////////////////// + [RFC 7159](http://rfc7159.net/rfc7159) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. - /// @name object inspection - /// Functions to inspect the type of a JSON value. - /// @{ + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. - /*! - @brief serialization + #### Storage - Serialization function for JSON values. The function tries to mimic - Python's `json.dumps()` function, and currently supports its @a indent - parameter. + Floating-point number values are stored directly inside a @ref basic_json + type. - @param[in] 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. - @param[in] indent_char The character to use for indentation if @a indent is - greater than `0`. The default is ` ` (space). + @sa @ref number_integer_t -- type for number values (integer) - @return string containing the serialization of the JSON value + @sa @ref number_unsigned_t -- type for number values (unsigned integer) - @complexity Linear. + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; - @liveexample{The following example shows the effect of different @a indent - parameters to the result of the serialization.,dump} + /// @} - @see https://docs.python.org/2/library/json.html#json.dump + private: - @since version 1.0.0; indentation character added in version 3.0.0 - */ - string_t dump(const int indent = -1, const char indent_char = ' ') const + /// helper for exception-safe object creation + template + static T* create(Args&& ... args) { - string_t result; - serializer s(output_adapter::create(result), indent_char); - - if (indent >= 0) - { - s.dump(*this, true, static_cast(indent)); - } - else + AllocatorType alloc; + auto deleter = [&](T * object) { - s.dump(*this, false, 0); - } - - return result; + alloc.deallocate(object, 1); + }; + std::unique_ptr object(alloc.allocate(1), deleter); + alloc.construct(object.get(), std::forward(args)...); + assert(object != nullptr); + return object.release(); } - /*! - @brief return the type of the JSON value (explicit) - - Return the type of the JSON value as a value from the @ref value_t - enumeration. + //////////////////////// + // JSON value storage // + //////////////////////// - @return the type of the JSON value + /*! + @brief a JSON value - @complexity Constant. + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + null | null | *no value is stored* - @liveexample{The following code exemplifies `type()` for all JSON - types.,type} + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. @since version 1.0.0 */ - constexpr value_t type() const noexcept + union json_value { - return m_type; - } + /// 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; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; - /*! - @brief return whether type is primitive + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } - This function returns true iff the JSON type is primitive (string, number, - boolean, or null). + case value_t::array: + { + array = create(); + break; + } - @return `true` if type is primitive (string, number, boolean, or null), - `false` otherwise. + case value_t::string: + { + string = create(""); + break; + } - @complexity Constant. + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } - @liveexample{The following code exemplifies `is_primitive()` for all JSON - types.,is_primitive} + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } - @sa @ref is_structured() -- returns whether JSON value is structured - @sa @ref is_null() -- returns whether JSON value is `null` - @sa @ref is_string() -- returns whether JSON value is a string - @sa @ref is_boolean() -- returns whether JSON value is a boolean - @sa @ref is_number() -- returns whether JSON value is a number + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } - @since version 1.0.0 + case value_t::null: + { + break; + } + + default: + { + if (JSON_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 2.1.1")); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + }; + + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. */ - constexpr bool is_primitive() const noexcept + void assert_invariant() const { - return is_null() or is_string() or is_boolean() or is_number(); + assert(m_type != value_t::object or m_value.object != nullptr); + assert(m_type != value_t::array or m_value.array != nullptr); + assert(m_type != value_t::string or m_value.string != nullptr); } - /*! - @brief return whether type is structured + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// - This function returns true iff the JSON type is structured (array or - object). + using parse_event_t = typename parser::parse_event_t; - @return `true` if type is structured (array or object), `false` otherwise. + /*! + @brief per-element parser callback type - @complexity Constant. + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse(std::istream&, const + parser_callback_t) or @ref parse(const CharT, const parser_callback_t), + it is called on certain events (passed as @ref parse_event_t via parameter + @a event) with a set recursion depth @a depth and context JSON value + @a parsed. The return value of the callback function is a boolean + indicating whether the element that emitted the callback shall be kept or + not. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. - @liveexample{The following code exemplifies `is_structured()` for all JSON - types.,is_structured} + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value - @sa @ref is_primitive() -- returns whether value is primitive - @sa @ref is_array() -- returns whether value is an array - @sa @ref is_object() -- returns whether value is an object + @image html callback_events.png "Example when certain parse events are triggered" - @since version 1.0.0 - */ - constexpr bool is_structured() const noexcept - { - return is_array() or is_object(); - } + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: - /*! - @brief return whether value is null + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. - This function returns true iff the JSON value is null. + @param[in] depth the depth of the recursion during parsing - @return `true` if type is null, `false` otherwise. + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called - @complexity Constant. + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. - @liveexample{The following code exemplifies `is_null()` for all JSON - types.,is_null} + @sa @ref parse(std::istream&, parser_callback_t) or + @ref parse(const CharT, const parser_callback_t) for examples @since version 1.0.0 */ - constexpr bool is_null() const noexcept - { - return m_type == value_t::null; - } + using parser_callback_t = typename parser::parser_callback_t; + + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ /*! - @brief return whether value is a boolean + @brief create an empty value with a given type - This function returns true iff the JSON value is a boolean. + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: - @return `true` if type is boolean, `false` otherwise. + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` - @complexity Constant. + @param[in] v the type of the value to create - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @complexity Constant. - @liveexample{The following code exemplifies `is_boolean()` for all JSON - types.,is_boolean} + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} @since version 1.0.0 */ - constexpr bool is_boolean() const noexcept + basic_json(const value_t v) + : m_type(v), m_value(v) { - return m_type == value_t::boolean; + assert_invariant(); } /*! - @brief return whether value is a number - - This function returns true iff the JSON value is a number. This includes - both integer and floating-point values. + @brief create a null object - @return `true` if type is number (regardless whether integer, unsigned - integer or floating-type), `false` otherwise. + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. @complexity Constant. - @exceptionsafety No-throw guarantee: this member function never throws + @exceptionsafety No-throw guarantee: this constructor never throws exceptions. - @liveexample{The following code exemplifies `is_number()` for all JSON - types.,is_number} - - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} @since version 1.0.0 */ - constexpr bool is_number() const noexcept + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) { - return is_number_integer() or is_number_float(); + assert_invariant(); } /*! - @brief return whether value is an integer number + @brief create a JSON value - This function returns true iff the JSON value is an integer or unsigned - integer number. This excludes floating-point values. + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exsits. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). - @return `true` if type is an integer or unsigned integer number, `false` - otherwise. + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::set`, `std::unordered_set`, `std::multiset`, and + `unordered_multiset` with a `value_type` from which a @ref basic_json + value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. - @complexity Constant. + See the examples below. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - @ref @ref json_serializer has a + `to_json(basic_json_t&, CompatibleType&&)` method - @liveexample{The following code exemplifies `is_number_integer()` for all - JSON types.,is_number_integer} + @tparam U = `uncvref_t` - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number - @sa @ref is_number_float() -- check if value is a floating-point number + @param[in] val the value to be forwarded - @since version 1.0.0 + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @throw what `json_serializer::to_json()` throws + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 */ - constexpr bool is_number_integer() const noexcept + template, + detail::enable_if_t::value and + not std::is_same::value and + not detail::is_basic_json_nested_type< + basic_json_t, U>::value and + detail::has_to_json::value, + int> = 0> + basic_json(CompatibleType && val) noexcept(noexcept(JSONSerializer::to_json( + std::declval(), std::forward(val)))) { - return m_type == value_t::number_integer or m_type == value_t::number_unsigned; + JSONSerializer::to_json(*this, std::forward(val)); + assert_invariant(); } /*! - @brief return whether value is an unsigned integer number + @brief create a container (array or object) from an initializer list - This function returns true iff the JSON value is an unsigned integer - number. This excludes floating-point and (signed) integer values. + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: - @return `true` if type is an unsigned integer number, `false` otherwise. + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. - @complexity Constant. + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has now way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. - @liveexample{The following code exemplifies `is_number_unsigned()` for all - JSON types.,is_number_unsigned} + With the rules described above, the following JSON values cannot be + expressed by an initializer list: - @sa @ref is_number() -- check if value is a number - @sa @ref is_number_integer() -- check if value is an integer or unsigned - integer number - @sa @ref is_number_float() -- check if value is a floating-point number + - the empty array (`[]`): use @ref array(std::initializer_list) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(std::initializer_list) with the same initializer list + in this case - @since version 2.0.0 - */ - constexpr bool is_number_unsigned() const noexcept - { - return m_type == value_t::number_unsigned; - } + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. - /*! - @brief return whether value is a floating-point number + @param[in] init initializer list with JSON values - This function returns true iff the JSON value is a floating-point number. - This excludes integer and unsigned integer values. + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(std::initializer_list) and + @ref object(std::initializer_list). - @return `true` if type is a floating-point number, `false` otherwise. + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect - @complexity Constant. + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(std::initializer_list) + for an example. - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @complexity Linear in the size of the initializer list @a init. - @liveexample{The following code exemplifies `is_number_float()` for all - JSON types.,is_number_float} + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} - @sa @ref is_number() -- check if value is number - @sa @ref is_number_integer() -- check if value is an integer number - @sa @ref is_number_unsigned() -- check if value is an unsigned integer - number + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list @since version 1.0.0 */ - constexpr bool is_number_float() const noexcept + basic_json(std::initializer_list init, + bool type_deduction = true, + value_t manual_type = value_t::array) { - return m_type == value_t::number_float; - } - - /*! - @brief return whether value is an object - - This function returns true iff the JSON value is an object. + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const basic_json & element) + { + return element.is_array() and element.size() == 2 and element[0].is_string(); + }); - @return `true` if type is object, `false` otherwise. + // adjust type if type deduction is not wanted + if (not type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } - @complexity Constant. + // if object is wanted but impossible, throw an exception + if (manual_type == value_t::object and not is_an_object) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list")); + } + } - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; - @liveexample{The following code exemplifies `is_object()` for all JSON - types.,is_object} + std::for_each(init.begin(), init.end(), [this](const basic_json & element) + { + m_value.object->emplace(*(element[0].m_value.string), element[1]); + }); + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init); + } - @since version 1.0.0 - */ - constexpr bool is_object() const noexcept - { - return m_type == value_t::object; + assert_invariant(); } /*! - @brief return whether value is an array + @brief explicitly create an array from an initializer list - This function returns true iff the JSON value is an array. + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. - @return `true` if type is array, `false` otherwise. + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(std::initializer_list, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object - @complexity Constant. + @param[in] init initializer list with JSON values to create an array from + (optional) - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @return JSON array value - @liveexample{The following code exemplifies `is_array()` for all JSON - types.,is_array} + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref object(std::initializer_list) -- create a JSON object + value from an initializer list @since version 1.0.0 */ - constexpr bool is_array() const noexcept + static basic_json array(std::initializer_list init = + std::initializer_list()) { - return m_type == value_t::array; + return basic_json(init, false, value_t::array); } /*! - @brief return whether value is a string + @brief explicitly create an object from an initializer list - This function returns true iff the JSON value is a string. + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. - @return `true` if type is string, `false` otherwise. + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(std::initializer_list), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(std::initializer_list, bool, value_t). - @complexity Constant. + @param[in] init initializer list to create an object from (optional) - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @return JSON object value - @liveexample{The following code exemplifies `is_string()` for all JSON - types.,is_string} + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(std::initializer_list, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa @ref basic_json(std::initializer_list, bool, value_t) -- + create a JSON value from an initializer list + @sa @ref array(std::initializer_list) -- create a JSON array + value from an initializer list @since version 1.0.0 */ - constexpr bool is_string() const noexcept + static basic_json object(std::initializer_list init = + std::initializer_list()) { - return m_type == value_t::string; + return basic_json(init, false, value_t::object); } /*! - @brief return whether value is discarded - - This function returns true iff the JSON value was discarded during parsing - with a callback function (see @ref parser_callback_t). - - @note This function will always be `false` for JSON values after parsing. - That is, discarded values can only occur during parsing, but will be - removed when inside a structured value or replaced by null in other cases. + @brief construct an array with count copies of given value - @return `true` if type is discarded, `false` otherwise. + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. As postcondition, + `std::distance(begin(),end()) == cnt` holds. - @complexity Constant. + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @complexity Linear in @a cnt. - @liveexample{The following code exemplifies `is_discarded()` for all JSON - types.,is_discarded} + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} @since version 1.0.0 */ - constexpr bool is_discarded() const noexcept + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) { - return m_type == value_t::discarded; + m_value.array = create(cnt, val); + assert_invariant(); } /*! - @brief return the type of the JSON value (implicit) + @brief construct a JSON container given an iterator range - Implicitly return the type of the JSON value as a value from the @ref - value_t enumeration. + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of primitive types (number, boolean, or string), @a first must + be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector`. + - In case of a null type, invalid_iterator.206 is thrown. - @return the type of the JSON value + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) - @complexity Constant. + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) - @exceptionsafety No-throw guarantee: this member function never throws - exceptions. + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion.** - @liveexample{The following code exemplifies the @ref value_t operator for - all JSON types.,operator__value_t} + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} @since version 1.0.0 */ - constexpr operator value_t() const noexcept + template::value or + std::is_same::value, int>::type = 0> + basic_json(InputIT first, InputIT last) { - return m_type; - } + assert(first.m_object != nullptr); + assert(last.m_object != nullptr); - /// @} + // make sure iterator fits the current value + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible")); + } - private: - ////////////////// - // value access // - ////////////////// + // copy type from first iterator + m_type = first.m_object->m_type; - /// get a boolean (explicit) - boolean_t get_impl(boolean_t* /*unused*/) const - { - if (is_boolean()) + // check if iterator range is complete for primitive values + switch (m_type) { - return m_value.boolean; - } + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } + break; + } - JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); - } + default: + { + break; + } + } - /// get a pointer to the value (object) - object_t* get_impl_ptr(object_t* /*unused*/) noexcept - { - return is_object() ? m_value.object : nullptr; - } + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } - /// get a pointer to the value (object) - constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept - { - return is_object() ? m_value.object : nullptr; - } + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } - /// get a pointer to the value (array) - array_t* get_impl_ptr(array_t* /*unused*/) noexcept - { - return is_array() ? m_value.array : nullptr; - } + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } - /// get a pointer to the value (array) - constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept - { - return is_array() ? m_value.array : nullptr; - } + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } - /// get a pointer to the value (string) - string_t* get_impl_ptr(string_t* /*unused*/) noexcept - { - return is_string() ? m_value.string : nullptr; - } + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } - /// get a pointer to the value (string) - constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept - { - return is_string() ? m_value.string : nullptr; - } + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } - /// get a pointer to the value (boolean) - boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } - /// get a pointer to the value (boolean) - constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept - { - return is_boolean() ? &m_value.boolean : nullptr; - } + default: + { + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + + first.m_object->type_name())); + } + } - /// get a pointer to the value (integer number) - number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; + assert_invariant(); } - /// get a pointer to the value (integer number) - constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept - { - return is_number_integer() ? &m_value.number_integer : nullptr; - } - /// get a pointer to the value (unsigned number) - number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// - /// get a pointer to the value (unsigned number) - constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept - { - return is_number_unsigned() ? &m_value.number_unsigned : nullptr; - } + /*! + @brief copy constructor - /// get a pointer to the value (floating-point number) - number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } + Creates a copy of a given JSON value. - /// get a pointer to the value (floating-point number) - constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept - { - return is_number_float() ? &m_value.number_float : nullptr; - } + @param[in] other the JSON value to copy - /*! - @brief helper function to implement get_ref() + @complexity Linear in the size of @a other. - This function helps to implement get_ref() without code duplication for - const and non-const overloads + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. - @tparam ThisType will be deduced as `basic_json` or `const basic_json` + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} - @throw type_error.303 if ReferenceType does not match underlying value - type of the current JSON + @since version 1.0.0 */ - template - static ReferenceType get_ref_impl(ThisType& obj) + basic_json(const basic_json& other) + : m_type(other.m_type) { - // helper type - using PointerType = typename std::add_pointer::type; - - // delegate the call to get_ptr<>() - auto ptr = obj.template get_ptr(); + // check of passed value is valid + other.assert_invariant(); - if (ptr != nullptr) + switch (m_type) { - return *ptr; - } + case value_t::object: + { + m_value = *other.m_value.object; + break; + } - JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); - } + case value_t::array: + { + m_value = *other.m_value.array; + break; + } - public: - /// @name value access - /// Direct access to the stored value of a JSON value. - /// @{ + case value_t::string: + { + m_value = *other.m_value.string; + break; + } - /*! - @brief get special-case overload + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } - This overloads avoids a lot of template boilerplate, it can be seen as the - identity method + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } - @tparam BasicJsonType == @ref basic_json + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } - @return a copy of *this + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } - @complexity Constant. + default: + { + break; + } + } - @since version 2.1.0 - */ - template < - typename BasicJsonType, - detail::enable_if_t::type, - basic_json_t>::value, - int> = 0 > - basic_json get() const - { - return *this; + assert_invariant(); } /*! - @brief get a value (explicit) - - Explicit type conversion between the JSON value and a compatible value - which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; - @endcode + @brief move constructor - This overloads is chosen if: - - @a ValueType is not @ref basic_json, - - @ref json_serializer has a `from_json()` method of the form - `void from_json(const basic_json&, ValueType&)`, and - - @ref json_serializer does not have a `from_json()` method of - the form `ValueType from_json(const basic_json&)` + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type + @param[in,out] other value to move to this object - @return copy of the JSON value, converted to @a ValueType + @post @a other is a JSON null value - @throw what @ref json_serializer `from_json()` method throws + @complexity Constant. - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,get__ValueType_const} + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} - @since version 2.1.0 + @since version 1.0.0 */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t < - not std::is_same::value and - detail::has_from_json::value and - not detail::has_non_default_from_json::value, - int > = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) { - // we cannot static_assert on ValueTypeCV being non-const, because - // there is support for get(), which is why we - // still need the uncvref - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - static_assert(std::is_default_constructible::value, - "types must be DefaultConstructible when used with get()"); + // check that passed value is valid + other.assert_invariant(); - ValueType ret; - JSONSerializer::from_json(*this, ret); - return ret; + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + assert_invariant(); } /*! - @brief get a value (explicit); special case - - Explicit type conversion between the JSON value and a compatible value - which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) - and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). - The value is converted by calling the @ref json_serializer - `from_json()` method. - - The function is equivalent to executing - @code {.cpp} - return JSONSerializer::from_json(*this); - @endcode + @brief copy assignment - This overloads is chosen if: - - @a ValueType is not @ref basic_json and - - @ref json_serializer has a `from_json()` method of the form - `ValueType from_json(const basic_json&)` + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the swap() member function. - @note If @ref json_serializer has both overloads of - `from_json()`, this one is chosen. + @param[in] other value to copy from - @tparam ValueTypeCV the provided value type - @tparam ValueType the returned value type + @complexity Linear. - @return copy of the JSON value, converted to @a ValueType + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. - @throw what @ref json_serializer `from_json()` method throws + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} - @since version 2.1.0 + @since version 1.0.0 */ - template < - typename ValueTypeCV, - typename ValueType = detail::uncvref_t, - detail::enable_if_t::value and - detail::has_non_default_from_json::value, int> = 0 > - ValueType get() const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + reference& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) { - static_assert(not std::is_reference::value, - "get() cannot be used with reference types, you might want to use get_ref()"); - return JSONSerializer::from_json(*this); + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + assert_invariant(); + return *this; } /*! - @brief get a pointer value (explicit) + @brief destructor - Explicit pointer access to the internally stored JSON value. No copies are - made. + Destroys the JSON value and frees all allocated memory. - @warning The pointer becomes invalid if the underlying JSON object - changes. + @complexity Linear. - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + @since version 1.0.0 + */ + ~basic_json() + { + assert_invariant(); - @complexity Constant. + switch (m_type) + { + case value_t::object: + { + AllocatorType alloc; + alloc.destroy(m_value.object); + alloc.deallocate(m_value.object, 1); + break; + } - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get__PointerType} + case value_t::array: + { + AllocatorType alloc; + alloc.destroy(m_value.array); + alloc.deallocate(m_value.array, 1); + break; + } - @sa @ref get_ptr() for explicit pointer-member access + case value_t::string: + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + break; + } - @since version 1.0.0 - */ - template::value, int>::type = 0> - PointerType get() noexcept - { - // delegate the call to get_ptr - return get_ptr(); + default: + { + // all other types need no specific destructor + break; + } + } } - /*! - @brief get a pointer value (explicit) - @copydoc get() - */ - template::value, int>::type = 0> - constexpr const PointerType get() const noexcept - { - // delegate the call to get_ptr - return get_ptr(); - } + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ /*! - @brief get a pointer value (implicit) + @brief serialization - Implicit pointer access to the internally stored JSON value. No copies are - made. + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + parameter. - @warning Writing data to the pointee of the result yields an undefined - state. + @param[in] 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. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). - @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref - object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, - @ref number_unsigned_t, or @ref number_float_t. Enforced by a static - assertion. + @return string containing the serialization of the JSON value - @return pointer to the internally stored JSON value if the requested - pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + @complexity Linear. - @complexity Constant. + @liveexample{The following example shows the effect of different @a indent + parameters to the result of the serialization.,dump} - @liveexample{The example below shows how pointers to internal values of a - JSON value can be requested. Note that no type conversions are made and a - `nullptr` is returned if the value and the requested pointer type does not - match.,get_ptr} + @see https://docs.python.org/2/library/json.html#json.dump - @since version 1.0.0 + @since version 1.0.0; indentation character added in version 3.0.0 */ - template::value, int>::type = 0> - PointerType get_ptr() noexcept + string_t dump(const int indent = -1, const char indent_char = ' ') const { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); + string_t result; + serializer s(detail::output_adapter_factory::create(result), indent_char); - // delegate the call to get_impl_ptr<>() - return get_impl_ptr(static_cast(nullptr)); + if (indent >= 0) + { + s.dump(*this, true, static_cast(indent)); + } + else + { + s.dump(*this, false, 0); + } + + return result; } /*! - @brief get a pointer value (implicit) - @copydoc get_ptr() + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @since version 1.0.0 */ - template::value and - std::is_const::type>::value, int>::type = 0> - constexpr const PointerType get_ptr() const noexcept + constexpr value_t type() const noexcept { - // get the type of the PointerType (remove pointer and const) - using pointee_t = typename std::remove_const::type>::type>::type; - // make sure the type matches the allowed types - static_assert( - std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - or std::is_same::value - , "incompatible pointer type"); - - // delegate the call to get_impl_ptr<>() const - return get_impl_ptr(static_cast(nullptr)); + return m_type; } /*! - @brief get a reference value (implicit) - - Implicit reference access to the internally stored JSON value. No copies - are made. + @brief return whether type is primitive - @warning Writing data to the referee of the result yields an undefined - state. + This function returns true iff the JSON type is primitive (string, number, + boolean, or null). - @tparam ReferenceType reference type; must be a reference to @ref array_t, - @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or - @ref number_float_t. Enforced by static assertion. + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. - @return reference to the internally stored JSON value if the requested - reference type @a ReferenceType fits to the JSON value; throws - type_error.303 otherwise + @complexity Constant. - @throw type_error.303 in case passed type @a ReferenceType is incompatible - with the stored JSON value; see example below + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant. + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} - @liveexample{The example shows several calls to `get_ref()`.,get_ref} + @sa @ref is_structured() -- returns whether JSON value is structured + @sa @ref is_null() -- returns whether JSON value is `null` + @sa @ref is_string() -- returns whether JSON value is a string + @sa @ref is_boolean() -- returns whether JSON value is a boolean + @sa @ref is_number() -- returns whether JSON value is a number - @since version 1.1.0 + @since version 1.0.0 */ - template::value, int>::type = 0> - ReferenceType get_ref() + constexpr bool is_primitive() const noexcept { - // delegate call to get_ref_impl - return get_ref_impl(*this); + return is_null() or is_string() or is_boolean() or is_number(); } /*! - @brief get a reference value (implicit) - @copydoc get_ref() - */ - template::value and - std::is_const::type>::value, int>::type = 0> - ReferenceType get_ref() const - { - // delegate call to get_ref_impl - return get_ref_impl(*this); - } + @brief return whether type is structured - /*! - @brief get a value (implicit) + This function returns true iff the JSON type is structured (array or + object). - Implicit type conversion between the JSON value and a compatible value. - The call is realized by calling @ref get() const. + @return `true` if type is structured (array or object), `false` otherwise. - @tparam ValueType non-pointer type compatible to the JSON value, for - instance `int` for JSON integer numbers, `bool` for JSON booleans, or - `std::vector` types for JSON arrays. The character type of @ref string_t - as well as an initializer list of this type is excluded to avoid - ambiguities as these types implicitly convert to `std::string`. - - @return copy of the JSON value, converted to type @a ValueType + @complexity Constant. - @throw type_error.302 in case passed type @a ValueType is incompatible - to the JSON value type (e.g., the JSON value is of type boolean, but a - string is requested); see example below + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Linear in the size of the JSON value. + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} - @liveexample{The example below shows several conversions from JSON values - to other types. There a few things to note: (1) Floating-point numbers can - be converted to integers\, (2) A JSON array can be converted to a standard - `std::vector`\, (3) A JSON object can be converted to C++ - associative containers such as `std::unordered_map`.,operator__ValueType} + @sa @ref is_primitive() -- returns whether value is primitive + @sa @ref is_array() -- returns whether value is an array + @sa @ref is_object() -- returns whether value is an object @since version 1.0.0 */ - template < typename ValueType, typename std::enable_if < - not std::is_pointer::value and - not std::is_same::value -#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 - and not std::is_same>::value -#endif -#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - and not std::is_same::value -#endif - , int >::type = 0 > - operator ValueType() const + constexpr bool is_structured() const noexcept { - // delegate the call to get<>() const - return get(); + return is_array() or is_object(); } - /// @} - - - //////////////////// - // element access // - //////////////////// - - /// @name element access - /// Access to the JSON value. - /// @{ - /*! - @brief access specified array element with bounds checking - - Returns a reference to the element at specified location @a idx, with - bounds checking. + @brief return whether value is null - @param[in] idx index of the element to access + This function returns true iff the JSON value is null. - @return reference to the element at index @a idx + @return `true` if type is null, `false` otherwise. - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant. + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__size_type} */ - reference at(size_type idx) + constexpr bool is_null() const noexcept { - // at only works for arrays - if (is_array()) - { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return m_type == value_t::null; } /*! - @brief access specified array element with bounds checking - - Returns a const reference to the element at specified location @a idx, - with bounds checking. + @brief return whether value is a boolean - @param[in] idx index of the element to access + This function returns true iff the JSON value is a boolean. - @return const reference to the element at index @a idx + @return `true` if type is boolean, `false` otherwise. - @throw type_error.304 if the JSON value is not an array; in this case, - calling `at` with an index makes no sense. See example below. - @throw out_of_range.401 if the index @a idx is out of range of the array; - that is, `idx >= size()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant. + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} @since version 1.0.0 - - @liveexample{The example below shows how array elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__size_type_const} */ - const_reference at(size_type idx) const + constexpr bool is_boolean() const noexcept { - // at only works for arrays - if (is_array()) - { - JSON_TRY - { - return m_value.array->at(idx); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return m_type == value_t::boolean; } /*! - @brief access specified object element with bounds checking - - Returns a reference to the element at with specified key @a key, with - bounds checking. + @brief return whether value is a number - @param[in] key key of the element to access + This function returns true iff the JSON value is a number. This includes + both integer and floating-point values. - @return reference to the element at key @a key + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Logarithmic in the size of the container. + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read and - written using `at()`. It also demonstrates the different exceptions that - can be thrown.,at__object_t_key_type} */ - reference at(const typename object_t::key_type& key) + constexpr bool is_number() const noexcept { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return is_number_integer() or is_number_float(); } /*! - @brief access specified object element with bounds checking - - Returns a const reference to the element at with specified key @a key, - with bounds checking. + @brief return whether value is an integer number - @param[in] key key of the element to access + This function returns true iff the JSON value is an integer or unsigned + integer number. This excludes floating-point values. - @return const reference to the element at key @a key + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. - @throw type_error.304 if the JSON value is not an object; in this case, - calling `at` with a key makes no sense. See example below. - @throw out_of_range.403 if the key @a key is is not stored in the object; - that is, `find(key) == end()`. See example below. + @complexity Constant. - @exceptionsafety Strong guarantee: if an exception is thrown, there are no - changes in the JSON value. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Logarithmic in the size of the container. + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference - @sa @ref value() for access by value with a default value + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa @ref is_number_float() -- check if value is a floating-point number @since version 1.0.0 - - @liveexample{The example below shows how object elements can be read using - `at()`. It also demonstrates the different exceptions that can be thrown., - at__object_t_key_type_const} */ - const_reference at(const typename object_t::key_type& key) const + constexpr bool is_number_integer() const noexcept { - // at only works for objects - if (is_object()) - { - JSON_TRY - { - return m_value.object->at(key); - } - JSON_CATCH (std::out_of_range&) - { - // create better exception explanation - JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); - } - } - else - { - JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); - } + return m_type == value_t::number_integer or m_type == value_t::number_unsigned; } /*! - @brief access specified array element + @brief return whether value is an unsigned integer number - Returns a reference to the element at specified location @a idx. + This function returns true iff the JSON value is an unsigned integer + number. This excludes floating-point and (signed) integer values. - @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), - then the array is silently filled up with `null` values to make `idx` a - valid reference to the last stored element. + @return `true` if type is an unsigned integer number, `false` otherwise. - @param[in] idx index of the element to access + @complexity Constant. - @return reference to the element at index @a idx - - @throw type_error.305 if the JSON value is not an array or null; in that - cases, using the [] operator with an index makes no sense. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant if @a idx is in the range of the array. Otherwise - linear in `idx - size()`. + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} - @liveexample{The example below shows how array elements can be read and - written using `[]` operator. Note the addition of `null` - values.,operatorarray__size_type} + @sa @ref is_number() -- check if value is a number + @sa @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa @ref is_number_float() -- check if value is a floating-point number - @since version 1.0.0 + @since version 2.0.0 */ - reference operator[](size_type idx) + constexpr bool is_number_unsigned() const noexcept { - // implicitly convert null value to an empty array - if (is_null()) - { - m_type = value_t::array; - m_value.array = create(); - assert_invariant(); - } - - // operator[] only works for arrays - if (is_array()) - { - // fill up array with null values if given idx is outside range - if (idx >= m_value.array->size()) - { - m_value.array->insert(m_value.array->end(), - idx - m_value.array->size() + 1, - basic_json()); - } - - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::number_unsigned; } /*! - @brief access specified array element + @brief return whether value is a floating-point number - Returns a const reference to the element at specified location @a idx. + This function returns true iff the JSON value is a floating-point number. + This excludes integer and unsigned integer values. - @param[in] idx index of the element to access + @return `true` if type is a floating-point number, `false` otherwise. - @return const reference to the element at index @a idx + @complexity Constant. - @throw type_error.305 if the JSON value is not an array; in that cases, - using the [] operator with an index makes no sense. + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @complexity Constant. + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} - @liveexample{The example below shows how array elements can be read using - the `[]` operator.,operatorarray__size_type_const} + @sa @ref is_number() -- check if value is number + @sa @ref is_number_integer() -- check if value is an integer number + @sa @ref is_number_unsigned() -- check if value is an unsigned integer + number @since version 1.0.0 */ - const_reference operator[](size_type idx) const + constexpr bool is_number_float() const noexcept { - // const operator[] only works for arrays - if (is_array()) - { - return m_value.array->operator[](idx); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::number_float; } /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access + @brief return whether value is an object - @return reference to the element at key @a key + This function returns true iff the JSON value is an object. - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @return `true` if type is object, `false` otherwise. - @complexity Logarithmic in the size of the container. + @complexity Constant. - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} @since version 1.0.0 */ - reference operator[](const typename object_t::key_type& key) + constexpr bool is_object() const noexcept { - // implicitly convert null value to an empty object - if (is_null()) - { - m_type = value_t::object; - m_value.object = create(); - assert_invariant(); - } - - // operator[] only works for objects - if (is_object()) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::object; } /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access - - @return const reference to the element at key @a key + @brief return whether value is an array - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** + This function returns true iff the JSON value is an array. - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @return `true` if type is array, `false` otherwise. - @complexity Logarithmic in the size of the container. + @complexity Constant. - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} @since version 1.0.0 */ - const_reference operator[](const typename object_t::key_type& key) const + constexpr bool is_array() const noexcept { - // const operator[] only works for objects - if (is_object()) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::array; } /*! - @brief access specified object element - - Returns a reference to the element at with specified key @a key. - - @note If @a key is not found in the object, then it is silently added to - the object and filled with a `null` value to make `key` a valid reference. - In case the value was `null` before, it is converted to an object. - - @param[in] key key of the element to access + @brief return whether value is a string - @return reference to the element at key @a key + This function returns true iff the JSON value is a string. - @throw type_error.305 if the JSON value is not an object or null; in that - cases, using the [] operator with a key makes no sense. + @return `true` if type is string, `false` otherwise. - @complexity Logarithmic in the size of the container. + @complexity Constant. - @liveexample{The example below shows how object elements can be read and - written using the `[]` operator.,operatorarray__key_type} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} - @since version 1.1.0 + @since version 1.0.0 */ - template - reference operator[](T* key) + constexpr bool is_string() const noexcept { - // implicitly convert null to object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } - - // at only works for objects - if (is_object()) - { - return m_value.object->operator[](key); - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::string; } /*! - @brief read-only access specified object element - - Returns a const reference to the element at with specified key @a key. No - bounds checking is performed. - - @warning If the element with key @a key does not exist, the behavior is - undefined. - - @param[in] key key of the element to access + @brief return whether value is discarded - @return const reference to the element at key @a key + This function returns true iff the JSON value was discarded during parsing + with a callback function (see @ref parser_callback_t). - @pre The element with key @a key must exist. **This precondition is - enforced with an assertion.** + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. - @throw type_error.305 if the JSON value is not an object; in that cases, - using the [] operator with a key makes no sense. + @return `true` if type is discarded, `false` otherwise. - @complexity Logarithmic in the size of the container. + @complexity Constant. - @liveexample{The example below shows how object elements can be read using - the `[]` operator.,operatorarray__key_type_const} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref value() for access by value with a default value + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} - @since version 1.1.0 + @since version 1.0.0 */ - template - const_reference operator[](T* key) const + constexpr bool is_discarded() const noexcept { - // at only works for objects - if (is_object()) - { - assert(m_value.object->find(key) != m_value.object->end()); - return m_value.object->find(key)->second; - } - - JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + return m_type == value_t::discarded; } /*! - @brief access specified object element with default value + @brief return the type of the JSON value (implicit) - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. - - The function is basically equivalent to executing - @code {.cpp} - try { - return at(key); - } catch(out_of_range) { - return default_value; - } - @endcode - - @note Unlike @ref at(const typename object_t::key_type&), this function - does not throw if the given key @a key was not found. - - @note Unlike @ref operator[](const typename object_t::key_type& key), this - function does not implicitly add an element to the position defined by @a - key. This function is furthermore also applicable to const objects. - - @param[in] key key of the element to access - @param[in] default_value the value to return if @a key is not found - - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. - - @return copy of the element at key @a key or @a default_value if @a key - is not found + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + @return the type of the JSON value - @complexity Logarithmic in the size of the container. + @complexity Constant. - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value} + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. - @sa @ref at(const typename object_t::key_type&) for access by reference - with range checking - @sa @ref operator[](const typename object_t::key_type&) for unchecked - access by reference + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} @since version 1.0.0 */ - template::value, int>::type = 0> - ValueType value(const typename object_t::key_type& key, ValueType default_value) const + constexpr operator value_t() const noexcept { - // at only works for objects - if (is_object()) - { - // if key is found, return value and given default value otherwise - const auto it = find(key); - if (it != end()) - { - return *it; - } + return m_type; + } - return default_value; - } - else + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (is_boolean()) { - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); + return m_value.boolean; } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + type_name())); } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const - */ - string_t value(const typename object_t::key_type& key, const char* default_value) const + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept { - return value(key, string_t(default_value)); + return is_object() ? m_value.object : nullptr; } - /*! - @brief access specified object element via JSON Pointer with default value - - Returns either a copy of an object's element at the specified key @a key - or a given default value if no element with key @a key exists. + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } - The function is basically equivalent to executing - @code {.cpp} - try { - return at(ptr); - } catch(out_of_range) { - return default_value; + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; } - @endcode - @note Unlike @ref at(const json_pointer&), this function does not throw - if the given key @a key was not found. + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } - @param[in] ptr a JSON pointer to the element to access - @param[in] default_value the value to return if @a ptr found no value + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } - @tparam ValueType type compatible to JSON values, for instance `int` for - JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for - JSON arrays. Note the type of the expected value at @a key and the default - value @a default_value must be compatible. + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } - @return copy of the element at key @a key or @a default_value if @a key - is not found + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } - @throw type_error.306 if the JSON value is not an objec; in that cases, - using `value()` with a key makes no sense. + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } - @complexity Logarithmic in the size of the container. + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } - @liveexample{The example below shows how object elements can be queried - with a default value.,basic_json__value_ptr} + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } - @sa @ref operator[](const json_pointer&) for unchecked access by reference + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } - @since version 2.0.2 - */ - template::value, int>::type = 0> - ValueType value(const json_pointer& ptr, ValueType default_value) const + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept { - // at only works for objects - if (is_object()) - { - // if pointer resolves a value, return it or use default value - JSON_TRY - { - return ptr.get_checked(this); - } - JSON_CATCH (out_of_range&) - { - return default_value; - } - } + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } - JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; } - /*! - @brief overload for a default value of type const char* - @copydoc basic_json::value(const json_pointer&, ValueType) const - */ - string_t value(const json_pointer& ptr, const char* default_value) const + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept { - return value(ptr, string_t(default_value)); + return is_number_float() ? &m_value.number_float : nullptr; } /*! - @brief access the first element - - Returns a reference to the first element in the container. For a JSON - container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - - @return In case of a structured type (array or object), a reference to the - first element is returned. In case of number, string, or boolean values, a - reference to the value is returned. + @brief helper function to implement get_ref() - @complexity Constant. + This function helps to implement get_ref() without code duplication for + const and non-const overloads - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. + @tparam ThisType will be deduced as `basic_json` or `const basic_json` - @throw invalid_iterator.214 when called on `null` value + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // helper type + using PointerType = typename std::add_pointer::type; - @liveexample{The following code shows an example for `front()`.,front} + // delegate the call to get_ptr<>() + auto ptr = obj.template get_ptr(); - @sa @ref back() -- access the last element + if (ptr != nullptr) + { + return *ptr; + } - @since version 1.0.0 - */ - reference front() - { - return *begin(); + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + obj.type_name())); } - /*! - @copydoc basic_json::front() - */ - const_reference front() const - { - return *cbegin(); - } + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ /*! - @brief access the last element + @brief get special-case overload - Returns a reference to the last element in the container. For a JSON - container `c`, the expression `c.back()` is equivalent to - @code {.cpp} - auto tmp = c.end(); - --tmp; - return *tmp; - @endcode - - @return In case of a structured type (array or object), a reference to the - last element is returned. In case of number, string, or boolean values, a - reference to the value is returned. - - @complexity Constant. - - @pre The JSON value must not be `null` (would throw `std::out_of_range`) - or an empty array or object (undefined behavior, **guarded by - assertions**). - @post The JSON value remains unchanged. - - @throw invalid_iterator.214 when called on a `null` value. See example - below. + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method - @liveexample{The following code shows an example for `back()`.,back} + @tparam BasicJsonType == @ref basic_json - @sa @ref front() -- access the first element + @return a copy of *this - @since version 1.0.0 - */ - reference back() - { - auto tmp = end(); - --tmp; - return *tmp; - } + @complexity Constant. - /*! - @copydoc basic_json::back() + @since version 2.1.0 */ - const_reference back() const + template < + typename BasicJsonType, + detail::enable_if_t::type, + basic_json_t>::value, + int> = 0 > + basic_json get() const { - auto tmp = cend(); - --tmp; - return *tmp; + return *this; } /*! - @brief remove element given an iterator - - Removes the element specified by iterator @a pos. The iterator @a pos must - be valid and dereferenceable. Thus the `end()` iterator (which is valid, - but is not dereferenceable) cannot be used as a value for @a pos. - - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. + @brief get a value (explicit) - @param[in] pos iterator to the element to remove - @return Iterator following the last removed element. If the iterator @a - pos refers to the last element, the `end()` iterator is returned. + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. - @tparam IteratorType an @ref iterator or @ref const_iterator + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.202 if called on an iterator which does not belong - to the current JSON value; example: `"iterator does not fit current - value"` - @throw invalid_iterator.205 if called on a primitive type with invalid - iterator (i.e., any iterator which is not `begin()`); example: `"iterator - out of range"` + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - @complexity The complexity depends on the type: - - objects: amortized constant - - arrays: linear in distance between @a pos and the end of the container - - strings: linear in the length of the string - - other types: constant + @return copy of the JSON value, converted to @a ValueType - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType} + @throw what @ref json_serializer `from_json()` method throws - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} - @since version 1.0.0 + @since version 2.1.0 */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType pos) + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t < + not std::is_same::value and + detail::has_from_json::value and + not detail::has_non_default_from_json::value, + int > = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) { - // make sure iterator fits the current value - if (this != pos.m_object) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + static_assert(std::is_default_constructible::value, + "types must be DefaultConstructible when used with get()"); - IteratorType result = end(); + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + } - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not pos.m_it.primitive_iterator.is_begin()) - { - JSON_THROW(invalid_iterator::create(205, "iterator out of range")); - } + /*! + @brief get a value (explicit); special case - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](http://en.cppreference.com/w/cpp/concept/CopyConstructible) + and **not** [DefaultConstructible](http://en.cppreference.com/w/cpp/concept/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. - m_type = value_t::null; - assert_invariant(); - break; - } + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); - break; - } + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); - break; - } + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } - } + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type - return result; + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < + typename ValueTypeCV, + typename ValueType = detail::uncvref_t, + detail::enable_if_t::value and + detail::has_non_default_from_json::value, int> = 0 > + ValueType get() const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + static_assert(not std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return JSONSerializer::from_json(*this); } /*! - @brief remove elements given an iterator range + @brief get a pointer value (explicit) - Removes the element specified by the range `[first; last)`. The iterator - @a first does not need to be dereferenceable if `first == last`: erasing - an empty range is a no-op. + Explicit pointer access to the internally stored JSON value. No copies are + made. - If called on a primitive type other than `null`, the resulting JSON value - will be `null`. + @warning The pointer becomes invalid if the underlying JSON object + changes. - @param[in] first iterator to the beginning of the range to remove - @param[in] last iterator past the end of the range to remove - @return Iterator following the last removed element. If the iterator @a - second refers to the last element, the `end()` iterator is returned. + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. - @tparam IteratorType an @ref iterator or @ref const_iterator + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - @post Invalidates iterators and references at or after the point of the - erase, including the `end()` iterator. + @complexity Constant. - @throw type_error.307 if called on a `null` value; example: `"cannot use - erase() with null"` - @throw invalid_iterator.203 if called on iterators which does not belong - to the current JSON value; example: `"iterators do not fit current value"` - @throw invalid_iterator.204 if called on a primitive type with invalid - iterators (i.e., if `first != begin()` and `last != end()`); example: - `"iterators out of range"` + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} - @complexity The complexity depends on the type: - - objects: `log(size()) + std::distance(first, last)` - - arrays: linear in the distance between @a first and @a last, plus linear - in the distance between @a last and end of the container - - strings: linear in the length of the string - - other types: constant - - @liveexample{The example shows the result of `erase()` for different JSON - types.,erase__IteratorType_IteratorType} - - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + @sa @ref get_ptr() for explicit pointer-member access @since version 1.0.0 */ - template::value or - std::is_same::value, int>::type - = 0> - IteratorType erase(IteratorType first, IteratorType last) + template::value, int>::type = 0> + PointerType get() noexcept { - // make sure iterator fits the current value - if (this != first.m_object or this != last.m_object) - { - JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); - } + // delegate the call to get_ptr + return get_ptr(); + } - IteratorType result = end(); + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, int>::type = 0> + constexpr const PointerType get() const noexcept + { + // delegate the call to get_ptr + return get_ptr(); + } - switch (m_type) - { - case value_t::boolean: - case value_t::number_float: - case value_t::number_integer: - case value_t::number_unsigned: - case value_t::string: - { - if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) - { - JSON_THROW(invalid_iterator::create(204, "iterators out of range")); - } + /*! + @brief get a pointer value (implicit) - if (is_string()) - { - AllocatorType alloc; - alloc.destroy(m_value.string); - alloc.deallocate(m_value.string, 1); - m_value.string = nullptr; - } + Implicit pointer access to the internally stored JSON value. No copies are + made. - m_type = value_t::null; - assert_invariant(); - break; - } + @warning Writing data to the pointee of the result yields an undefined + state. - case value_t::object: - { - result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, - last.m_it.object_iterator); - break; - } + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. - case value_t::array: - { - result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, - last.m_it.array_iterator); - break; - } + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise - default: - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } - } + @complexity Constant. - return result; + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + PointerType get_ptr() noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); + + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); } /*! - @brief remove element from a JSON object given a key + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + constexpr const PointerType get_ptr() const noexcept + { + // get the type of the PointerType (remove pointer and const) + using pointee_t = typename std::remove_const::type>::type>::type; + // make sure the type matches the allowed types + static_assert( + std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + or std::is_same::value + , "incompatible pointer type"); - Removes elements from a JSON object with the key value @a key. + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } - @param[in] key value of the elements to remove + /*! + @brief get a reference value (implicit) - @return Number of elements removed. If @a ObjectType is the default - `std::map` type, the return value will always be `0` (@a key was not - found) or `1` (@a key was found). + Implicit reference access to the internally stored JSON value. No copies + are made. - @post References and iterators to the erased elements are invalidated. - Other references and iterators are not affected. + @warning Writing data to the referee of the result yields an undefined + state. - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. - @complexity `log(size()) + count(key)` + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise - @liveexample{The example shows the effect of `erase()`.,erase__key_type} + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const size_type) -- removes the element from an array at - the given index + @complexity Constant. - @since version 1.0.0 + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 */ - size_type erase(const typename object_t::key_type& key) + template::value, int>::type = 0> + ReferenceType get_ref() { - // this erase only works for objects - if (is_object()) - { - return m_value.object->erase(key); - } + // delegate call to get_ref_impl + return get_ref_impl(*this); + } - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template::value and + std::is_const::type>::value, int>::type = 0> + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); } /*! - @brief remove element from a JSON array given an index + @brief get a value (implicit) - Removes element from a JSON array at the index @a idx. + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. - @param[in] idx index of the element to remove + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. - @throw type_error.307 when called on a type other than JSON object; - example: `"cannot use erase() with null"` - @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 - is out of range"` + @return copy of the JSON value, converted to type @a ValueType - @complexity Linear in distance between @a idx and the end of the container. + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below - @liveexample{The example shows the effect of `erase()`.,erase__size_type} + @complexity Linear in the size of the JSON value. - @sa @ref erase(IteratorType) -- removes the element at a given position - @sa @ref erase(IteratorType, IteratorType) -- removes the elements in - the given range - @sa @ref erase(const typename object_t::key_type&) -- removes the element - from an object at the given key + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} @since version 1.0.0 */ - void erase(const size_type idx) + template < typename ValueType, typename std::enable_if < + not std::is_pointer::value and + not std::is_same::value +#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015 + and not std::is_same>::value +#endif +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_MSC_VER) && _MSC_VER >1900 && defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + and not std::is_same::value +#endif + , int >::type = 0 > + operator ValueType() const { - // this erase only works for arrays - if (is_array()) - { - if (idx >= size()) - { - JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); - } - - m_value.array->erase(m_value.array->begin() + static_cast(idx)); - } - else - { - JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); - } + // delegate the call to get<>() const + return get(); } /// @} - //////////// - // lookup // - //////////// + //////////////////// + // element access // + //////////////////// - /// @name lookup + /// @name element access + /// Access to the JSON value. /// @{ /*! - @brief find an element in a JSON object + @brief access specified array element with bounds checking - Finds an element in a JSON object with key equivalent to @a key. If the - element is not found or the JSON value is not an object, end() is - returned. + Returns a reference to the element at specified location @a idx, with + bounds checking. - @note This method always returns @ref end() when executed on a JSON type - that is not an object. + @param[in] idx index of the element to access - @param[in] key key value of the element to search for + @return reference to the element at index @a idx - @return Iterator to an element with key equivalent to @a key. If no such - element is found or the JSON value is not an object, past-the-end (see - @ref end()) iterator is returned. + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. - @complexity Logarithmic in the size of the JSON object. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The example shows how `find()` is used.,find__key_type} + @complexity Constant. @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} */ - iterator find(typename object_t::key_type key) + reference at(size_type idx) { - auto result = end(); - - if (is_object()) + // at only works for arrays + if (is_array()) { - result.m_it.object_iterator = m_value.object->find(key); + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } } - - return result; - } - - /*! - @brief find an element in a JSON object - @copydoc find(typename object_t::key_type) - */ - const_iterator find(typename object_t::key_type key) const - { - auto result = cend(); - - if (is_object()) + else { - result.m_it.object_iterator = m_value.object->find(key); + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); } - - return result; } /*! - @brief returns the number of occurrences of a key in a JSON object + @brief access specified array element with bounds checking - Returns the number of elements with key @a key. If ObjectType is the - default `std::map` type, the return value will always be `0` (@a key was - not found) or `1` (@a key was found). + Returns a const reference to the element at specified location @a idx, + with bounds checking. - @note This method always returns `0` when executed on a JSON type that is - not an object. + @param[in] idx index of the element to access - @param[in] key key value of the element to count + @return const reference to the element at index @a idx - @return Number of elements with key @a key. If the JSON value is not an - object, the return value will be `0`. + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. - @complexity Logarithmic in the size of the JSON object. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The example shows how `count()` is used.,count} + @complexity Constant. @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} */ - size_type count(typename object_t::key_type key) const + const_reference at(size_type idx) const { - // return 0 for all nonobject types - return is_object() ? m_value.object->count(key) : 0; + // at only works for arrays + if (is_array()) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } - /// @} - - - /////////////// - // iterators // - /////////////// - - /// @name iterators - /// @{ - /*! - @brief returns an iterator to the first element + @brief access specified object element with bounds checking - Returns an iterator to the first element. + Returns a reference to the element at with specified key @a key, with + bounds checking. - @image html range-begin-end.svg "Illustration from cppreference.com" + @param[in] key key of the element to access - @return iterator to the first element + @return reference to the element at key @a key - @complexity Constant. + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The following code shows an example for `begin()`.,begin} + @complexity Logarithmic in the size of the container. - @sa @ref cbegin() -- returns a const iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value @since version 1.0.0 - */ - iterator begin() noexcept - { - iterator result(this); - result.set_begin(); - return result; - } - /*! - @copydoc basic_json::cbegin() + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} */ - const_iterator begin() const noexcept + reference at(const typename object_t::key_type& key) { - return cbegin(); + // at only works for objects + if (is_object()) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns a const iterator to the first element + @brief access specified object element with bounds checking - Returns a const iterator to the first element. + Returns a const reference to the element at with specified key @a key, + with bounds checking. - @image html range-begin-end.svg "Illustration from cppreference.com" + @param[in] key key of the element to access - @return const iterator to the first element + @return const reference to the element at key @a key - @complexity Constant. + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).begin()`. + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. - @liveexample{The following code shows an example for `cbegin()`.,cbegin} + @complexity Logarithmic in the size of the container. - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref end() -- returns an iterator to the end - @sa @ref cend() -- returns a const iterator to the end + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa @ref value() for access by value with a default value @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} */ - const_iterator cbegin() const noexcept + const_reference at(const typename object_t::key_type& key) const { - const_iterator result(this); - result.set_begin(); - return result; - } - - /*! - @brief returns an iterator to one past the last element - - Returns an iterator to one past the last element. - - @image html range-begin-end.svg "Illustration from cppreference.com" - - @return iterator one past the last element - - @complexity Constant. - - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - @liveexample{The following code shows an example for `end()`.,end} - - @sa @ref cend() -- returns a const iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning - - @since version 1.0.0 - */ - iterator end() noexcept - { - iterator result(this); - result.set_end(); - return result; - } - - /*! - @copydoc basic_json::cend() - */ - const_iterator end() const noexcept - { - return cend(); + // at only works for objects + if (is_object()) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found")); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + type_name())); + } } /*! - @brief returns a const iterator to one past the last element + @brief access specified array element - Returns a const iterator to one past the last element. + Returns a reference to the element at specified location @a idx. - @image html range-begin-end.svg "Illustration from cppreference.com" + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. - @return const iterator one past the last element + @param[in] idx index of the element to access - @complexity Constant. + @return reference to the element at index @a idx - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).end()`. + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. - @liveexample{The following code shows an example for `cend()`.,cend} + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. - @sa @ref end() -- returns an iterator to the end - @sa @ref begin() -- returns an iterator to the beginning - @sa @ref cbegin() -- returns a const iterator to the beginning + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} @since version 1.0.0 */ - const_iterator cend() const noexcept + reference operator[](size_type idx) { - const_iterator result(this); - result.set_end(); - return result; + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (is_array()) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { + m_value.array->insert(m_value.array->end(), + idx - m_value.array->size() + 1, + basic_json()); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns an iterator to the reverse-beginning + @brief access specified array element - Returns an iterator to the reverse-beginning; that is, the last element. + Returns a const reference to the element at specified location @a idx. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @param[in] idx index of the element to access - @complexity Constant. + @return const reference to the element at index @a idx - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(end())`. + @throw type_error.305 if the JSON value is not an array; in that cases, + using the [] operator with an index makes no sense. - @liveexample{The following code shows an example for `rbegin()`.,rbegin} + @complexity Constant. - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} @since version 1.0.0 */ - reverse_iterator rbegin() noexcept + const_reference operator[](size_type idx) const { - return reverse_iterator(end()); - } + // const operator[] only works for arrays + if (is_array()) + { + return m_value.array->operator[](idx); + } - /*! - @copydoc basic_json::crbegin() - */ - const_reverse_iterator rbegin() const noexcept - { - return crbegin(); + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns an iterator to the reverse-end + @brief access specified object element - Returns an iterator to the reverse-end; that is, one before the first - element. + Returns a reference to the element at with specified key @a key. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. - @complexity Constant. + @param[in] key key of the element to access - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `reverse_iterator(begin())`. + @return reference to the element at key @a key - @liveexample{The following code shows an example for `rend()`.,rend} + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. - @sa @ref crend() -- returns a const reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value @since version 1.0.0 */ - reverse_iterator rend() noexcept + reference operator[](const typename object_t::key_type& key) { - return reverse_iterator(begin()); - } + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } - /*! - @copydoc basic_json::crend() - */ - const_reverse_iterator rend() const noexcept - { - return crend(); + // operator[] only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns a const reverse iterator to the last element + @brief read-only access specified object element - Returns a const iterator to the reverse-beginning; that is, the last - element. + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @warning If the element with key @a key does not exist, the behavior is + undefined. - @complexity Constant. + @param[in] key key of the element to access - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rbegin()`. + @return const reference to the element at key @a key - @liveexample{The following code shows an example for `crbegin()`.,crbegin} + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref crend() -- returns a const reverse iterator to the end + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value @since version 1.0.0 */ - const_reverse_iterator crbegin() const noexcept + const_reference operator[](const typename object_t::key_type& key) const { - return const_reverse_iterator(cend()); + // const operator[] only works for objects + if (is_object()) + { + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); } /*! - @brief returns a const reverse iterator to one before the first + @brief access specified object element - Returns a const reverse iterator to the reverse-end; that is, one before - the first element. + Returns a reference to the element at with specified key @a key. - @image html range-rbegin-rend.svg "Illustration from cppreference.com" + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. - @complexity Constant. + @param[in] key key of the element to access - @requirement This function helps `basic_json` satisfying the - [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) - requirements: - - The complexity is constant. - - Has the semantics of `const_cast(*this).rend()`. + @return reference to the element at key @a key - @liveexample{The following code shows an example for `crend()`.,crend} - - @sa @ref rend() -- returns a reverse iterator to the end - @sa @ref rbegin() -- returns a reverse iterator to the beginning - @sa @ref crbegin() -- returns a const reverse iterator to the beginning - - @since version 1.0.0 - */ - const_reverse_iterator crend() const noexcept - { - return const_reverse_iterator(cbegin()); - } - - public: - /*! - @brief wrapper to access iterator member functions in range-based for + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. - This function allows to access @ref iterator::key() and @ref - iterator::value() during range-based for loops. In these loops, a - reference to the JSON values is returned, so there is no access to the - underlying iterator. + @complexity Logarithmic in the size of the container. - @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} - @note The name of this function is not yet final and may change in the - future. - */ - static iteration_proxy iterator_wrapper(reference cont) - { - return iteration_proxy(cont); - } + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value - /*! - @copydoc iterator_wrapper(reference) + @since version 1.1.0 */ - static iteration_proxy iterator_wrapper(const_reference cont) + template + reference operator[](T* key) { - return iteration_proxy(cont); - } + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } - /// @} + // at only works for objects + if (is_object()) + { + return m_value.object->operator[](key); + } + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + } - ////////////// - // capacity // - ////////////// + /*! + @brief read-only access specified object element - /// @name capacity - /// @{ + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. - /*! - @brief checks whether the container is empty + @warning If the element with key @a key does not exist, the behavior is + undefined. - Checks if a JSON value has no elements. + @param[in] key key of the element to access - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `true` - boolean | `false` - string | `false` - number | `false` - object | result of function `object_t::empty()` - array | result of function `array_t::empty()` + @return const reference to the element at key @a key - @note This function does not return whether a string stored as JSON value - is empty - it returns whether the JSON container itself is empty which is - false in the case of a string. + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `empty()` functions have constant - complexity. + @throw type_error.305 if the JSON value is not an object; in that cases, + using the [] operator with a key makes no sense. - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `begin() == end()`. + @complexity Logarithmic in the size of the container. - @liveexample{The following code uses `empty()` to check if a JSON - object contains any elements.,empty} + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} - @sa @ref size() -- returns the number of elements + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref value() for access by value with a default value - @since version 1.0.0 + @since version 1.1.0 */ - bool empty() const noexcept + template + const_reference operator[](T* key) const { - switch (m_type) + // at only works for objects + if (is_object()) { - case value_t::null: - { - // null values are empty - return true; - } + assert(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } - case value_t::array: - { - // delegate call to array_t::empty() - return m_value.array->empty(); - } + JSON_THROW(type_error::create(305, "cannot use operator[] with " + type_name())); + } - case value_t::object: - { - // delegate call to object_t::empty() - return m_value.object->empty(); - } + /*! + @brief access specified object element with default value - default: - { - // all other types are nonempty - return false; - } - } + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; } + @endcode - /*! - @brief returns the number of elements + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. - Returns the number of elements in a JSON value. + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` - boolean | `1` - string | `1` - number | `1` - object | result of function object_t::size() - array | result of function array_t::size() + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found - @note This function does not return the length of a string stored as JSON - value - it returns the number of elements in the JSON value which is 1 in - the case of a string. + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their size() functions have constant - complexity. + @return copy of the element at key @a key or @a default_value if @a key + is not found - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of `std::distance(begin(), end())`. + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. - @liveexample{The following code calls `size()` on the different value - types.,size} + @complexity Logarithmic in the size of the container. - @sa @ref empty() -- checks whether the container is empty - @sa @ref max_size() -- returns the maximal number of elements + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa @ref operator[](const typename object_t::key_type&) for unchecked + access by reference @since version 1.0.0 */ - size_type size() const noexcept + template::value, int>::type = 0> + ValueType value(const typename object_t::key_type& key, ValueType default_value) const { - switch (m_type) + // at only works for objects + if (is_object()) { - case value_t::null: - { - // null values are empty - return 0; - } - - case value_t::array: - { - // delegate call to array_t::size() - return m_value.array->size(); - } - - case value_t::object: + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) { - // delegate call to object_t::size() - return m_value.object->size(); + return *it; } - default: - { - // all other types have size 1 - return 1; - } + return default_value; + } + else + { + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } } /*! - @brief returns the maximum possible number of elements + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, ValueType) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } - Returns the maximum number of elements a JSON value is able to hold due to - system or library implementation limitations, i.e. `std::distance(begin(), - end())` for the JSON value. + /*! + @brief access specified object element via JSON Pointer with default value - @return The return value depends on the different types and is - defined as follows: - Value type | return value - ----------- | ------------- - null | `0` (same as `size()`) - boolean | `1` (same as `size()`) - string | `1` (same as `size()`) - number | `1` (same as `size()`) - object | result of function `object_t::max_size()` - array | result of function `array_t::max_size()` + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. - @complexity Constant, as long as @ref array_t and @ref object_t satisfy - the Container concept; that is, their `max_size()` functions have constant - complexity. + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode - @requirement This function helps `basic_json` satisfying the - [Container](http://en.cppreference.com/w/cpp/concept/Container) - requirements: - - The complexity is constant. - - Has the semantics of returning `b.size()` where `b` is the largest - possible JSON value. + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. - @liveexample{The following code calls `max_size()` on the different value - types. Note the output is implementation specific.,max_size} + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value - @sa @ref size() -- returns the number of elements + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. - @since version 1.0.0 + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.306 if the JSON value is not an objec; in that cases, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 */ - size_type max_size() const noexcept + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, ValueType default_value) const { - switch (m_type) + // at only works for objects + if (is_object()) { - case value_t::array: - { - // delegate call to array_t::max_size() - return m_value.array->max_size(); - } - - case value_t::object: + // if pointer resolves a value, return it or use default value + JSON_TRY { - // delegate call to object_t::max_size() - return m_value.object->max_size(); + return ptr.get_checked(this); } - - default: + JSON_CATCH (out_of_range&) { - // all other types have max_size() == size() - return size(); + return default_value; } } + + JSON_THROW(type_error::create(306, "cannot use value() with " + type_name())); } - /// @} + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + /*! + @brief access the first element - /////////////// - // modifiers // - /////////////// + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. - /// @name modifiers - /// @{ + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, or boolean values, a + reference to the value is returned. - /*! - @brief clears the contents + @complexity Constant. - Clears the content of a JSON value and resets it to the default value as - if @ref basic_json(value_t) would have been called: + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - Value type | initial value - ----------- | ------------- - null | `null` - boolean | `false` - string | `""` - number | `0` - object | `{}` - array | `[]` + @throw invalid_iterator.214 when called on `null` value - @complexity Linear in the size of the JSON value. + @liveexample{The following code shows an example for `front()`.,front} - @liveexample{The example below shows the effect of `clear()` to different - JSON types.,clear} + @sa @ref back() -- access the last element @since version 1.0.0 */ - void clear() noexcept + reference front() { - switch (m_type) - { - case value_t::number_integer: - { - m_value.number_integer = 0; - break; - } - - case value_t::number_unsigned: - { - m_value.number_unsigned = 0; - break; - } - - case value_t::number_float: - { - m_value.number_float = 0.0; - break; - } - - case value_t::boolean: - { - m_value.boolean = false; - 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; - } + return *begin(); + } - default: - { - break; - } - } + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); } /*! - @brief add an object to an array + @brief access the last element - Appends the given element @a val to the end of the JSON value. If the - function is called on a JSON null value, an empty array is created before - appending @a val. + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode - @param[in] val the value to add to the JSON array + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, or boolean values, a + reference to the value is returned. - @throw type_error.308 when called on a type other than JSON array or - null; example: `"cannot use push_back() with number"` + @complexity Constant. - @complexity Amortized constant. + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON array. Note how the `null` value was silently - converted to a JSON array.,push_back} + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa @ref front() -- access the first element @since version 1.0.0 */ - void push_back(basic_json&& val) + reference back() { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } - - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } - - // add element to array (move semantics) - m_value.array->push_back(std::move(val)); - // invalidate object - val.m_type = value_t::null; + auto tmp = end(); + --tmp; + return *tmp; } /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) + @copydoc basic_json::back() */ - reference operator+=(basic_json&& val) + const_reference back() const { - push_back(std::move(val)); - return *this; + auto tmp = cend(); + --tmp; + return *tmp; } /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - void push_back(const basic_json& val) - { - // push_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); - } + @brief remove element given an iterator - // transform null object into an array - if (is_null()) - { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); - } + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. - // add element to array - m_value.array->push_back(val); - } + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. - /*! - @brief add an object to an array - @copydoc push_back(basic_json&&) - */ - reference operator+=(const basic_json& val) - { - push_back(val); - return *this; - } + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. - /*! - @brief add an object to an object + @tparam IteratorType an @ref iterator or @ref const_iterator - Inserts the given element @a val to the JSON object. If the function is - called on a JSON null value, an empty object is created before inserting - @a val. + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. - @param[in] val the value to add to the JSON object + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` - @throw type_error.308 when called on a type other than JSON object or - null; example: `"cannot use push_back() with number"` + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings: linear in the length of the string + - other types: constant - @complexity Logarithmic in the size of the container, O(log(`size()`)). + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} - @liveexample{The example shows how `push_back()` and `+=` can be used to - add elements to a JSON object. Note how the `null` value was silently - converted to a JSON object.,push_back__object_t__value} + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ - void push_back(const typename object_t::value_type& val) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType pos) { - // push_back only works for null objects or objects - if (not(is_null() or is_object())) + // make sure iterator fits the current value + if (this != pos.m_object) { - JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); - } + IteratorType result = end(); - // add element to array - m_value.object->insert(val); - } + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not pos.m_it.primitive_iterator.is_begin()) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range")); + } - /*! - @brief add an object to an object - @copydoc push_back(const typename object_t::value_type&) - */ - reference operator+=(const typename object_t::value_type& val) - { - push_back(val); - return *this; - } + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } - /*! - @brief add an object to an object + m_type = value_t::null; + assert_invariant(); + break; + } - This function allows to use `push_back` with an initializer list. In case + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } - 1. the current value is an object, - 2. the initializer list @a init contains only two elements, and - 3. the first element of @a init is a string, + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } - @a init is converted into an object element and added using - @ref push_back(const typename object_t::value_type&). Otherwise, @a init - is converted to a JSON value and added using @ref push_back(basic_json&&). + default: + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + } + } - @param[in] init an initializer list + return result; + } - @complexity Linear in the size of the initializer list @a init. + /*! + @brief remove elements given an iterator range - @note This function is required to resolve an ambiguous overload error, - because pairs like `{"key", "value"}` can be both interpreted as - `object_t::value_type` or `std::initializer_list`, see - https://github.com/nlohmann/json/issues/235 for more information. + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. - @liveexample{The example shows how initializer lists are treated as - objects when possible.,push_back__initializer_list} - */ - void push_back(std::initializer_list init) - { - if (is_object() and init.size() == 2 and init.begin()->is_string()) - { - const string_t key = *init.begin(); - push_back(typename object_t::value_type(key, *(init.begin() + 1))); - } - else - { - push_back(basic_json(init)); - } - } + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. - /*! - @brief add an object to an object - @copydoc push_back(std::initializer_list) - */ - reference operator+=(std::initializer_list init) - { - push_back(init); - return *this; - } + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. - /*! - @brief add an object to an array + @tparam IteratorType an @ref iterator or @ref const_iterator - Creates a JSON value from the passed parameters @a args to the end of the - JSON value. If the function is called on a JSON null value, an empty array - is created before appending the value created from @a args. + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` - @throw type_error.311 when called on a type other than JSON array or - null; example: `"cannot use emplace_back() with number"` + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings: linear in the length of the string + - other types: constant - @complexity Amortized constant. + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} - @liveexample{The example shows how `push_back()` can be used to add - elements to a JSON array. Note how the `null` value was silently converted - to a JSON array.,emplace_back} + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa @ref erase(const size_type) -- removes the element from an array at + the given index - @since version 2.0.8 + @since version 1.0.0 */ - template - void emplace_back(Args&& ... args) + template::value or + std::is_same::value, int>::type + = 0> + IteratorType erase(IteratorType first, IteratorType last) { - // emplace_back only works for null objects or arrays - if (not(is_null() or is_array())) - { - JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); - } - - // transform null object into an array - if (is_null()) + // make sure iterator fits the current value + if (this != first.m_object or this != last.m_object) { - m_type = value_t::array; - m_value = value_t::array; - assert_invariant(); + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value")); } - // add element to array (perfect forwarding) - m_value.array->emplace_back(std::forward(args)...); - } - - /*! - @brief add an object to an object if key does not exist + IteratorType result = end(); - Inserts a new element into a JSON object constructed in-place with the - given @a args if there is no element with the key in the container. If the - function is called on a JSON null value, an empty object is created before - appending the value created from @a args. - - @param[in] args arguments to forward to a constructor of @ref basic_json - @tparam Args compatible types to create a @ref basic_json object - - @return a pair consisting of an iterator to the inserted element, or the - already-existing element if no insertion happened, and a bool - denoting whether the insertion took place. + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end()) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range")); + } - @throw type_error.311 when called on a type other than JSON object or - null; example: `"cannot use emplace() with number"` + if (is_string()) + { + AllocatorType alloc; + alloc.destroy(m_value.string); + alloc.deallocate(m_value.string, 1); + m_value.string = nullptr; + } - @complexity Logarithmic in the size of the container, O(log(`size()`)). + m_type = value_t::null; + assert_invariant(); + break; + } - @liveexample{The example shows how `emplace()` can be used to add elements - to a JSON object. Note how the `null` value was silently converted to a - JSON object. Further note how no value is added if there was already one - value stored with the same key.,emplace} + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } - @since version 2.0.8 - */ - template - std::pair emplace(Args&& ... args) - { - // emplace only works for null objects or arrays - if (not(is_null() or is_object())) - { - JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); - } + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } - // transform null object into an object - if (is_null()) - { - m_type = value_t::object; - m_value = value_t::object; - assert_invariant(); + default: + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); + } } - // add element to array (perfect forwarding) - auto res = m_value.object->emplace(std::forward(args)...); - // create result iterator and set iterator to the result of emplace - auto it = begin(); - it.m_it.object_iterator = res.first; - - // return pair of iterator and boolean - return {it, res.second}; + return result; } /*! - @brief inserts element + @brief remove element from a JSON object given a key - Inserts element @a val before iterator @a pos. + Removes elements from a JSON object with the key value @a key. - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] val element to insert - @return iterator pointing to the inserted @a val. + @param[in] key value of the elements to remove - @throw type_error.309 if called on JSON values other than arrays; - example: `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). - @complexity Constant plus linear in the distance between @a pos and end of - the container. + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. - @liveexample{The example shows how `insert()` is used.,insert} + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const size_type) -- removes the element from an array at + the given index @since version 1.0.0 */ - iterator insert(const_iterator pos, const basic_json& val) + size_type erase(const typename object_t::key_type& key) { - // insert only works for arrays - if (is_array()) + // this erase only works for objects + if (is_object()) { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } - - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); - return result; + return m_value.object->erase(key); } - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } /*! - @brief inserts element - @copydoc insert(const_iterator, const basic_json&) - */ - iterator insert(const_iterator pos, basic_json&& val) - { - return insert(pos, val); - } + @brief remove element from a JSON array given an index - /*! - @brief inserts elements + Removes element from a JSON array at the index @a idx. - Inserts @a cnt copies of @a val before iterator @a pos. + @param[in] idx index of the element to remove - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] cnt number of copies of @a val to insert - @param[in] val element to insert - @return iterator pointing to the first element inserted, or @a pos if - `cnt==0` + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @complexity Linear in distance between @a idx and the end of the container. - @complexity Linear in @a cnt plus linear in the distance between @a pos - and end of the container. + @liveexample{The example shows the effect of `erase()`.,erase__size_type} - @liveexample{The example shows how `insert()` is used.,insert__count} + @sa @ref erase(IteratorType) -- removes the element at a given position + @sa @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key @since version 1.0.0 */ - iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + void erase(const size_type idx) { - // insert only works for arrays + // this erase only works for arrays if (is_array()) { - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + if (idx >= size()) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range")); } - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); - return result; + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + type_name())); } - - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + /*! - @brief inserts elements + @brief find an element in a JSON object - Inserts elements from range `[first, last)` before iterator @a pos. + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert + @note This method always returns @ref end() when executed on a JSON type + that is not an object. - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` - @throw invalid_iterator.211 if @a first or @a last are iterators into - container for which insert is called; example: `"passed iterators may not - belong to container"` + @param[in] key key value of the element to search for - @return iterator pointing to the first element inserted, or @a pos if - `first==last` + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. - @complexity Linear in `std::distance(first, last)` plus linear in the - distance between @a pos and end of the container. + @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how `insert()` is used.,insert__range} + @liveexample{The example shows how `find()` is used.,find__key_type} @since version 1.0.0 */ - iterator insert(const_iterator pos, const_iterator first, const_iterator last) + iterator find(typename object_t::key_type key) { - // insert only works for arrays - if (not is_array()) + auto result = end(); + + if (is_object()) { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + result.m_it.object_iterator = m_value.object->find(key); } - // check if iterator pos fits to this JSON value - if (pos.m_object != this) + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(typename object_t::key_type) + */ + const_iterator find(typename object_t::key_type key) const + { + auto result = cend(); + + if (is_object()) { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + result.m_it.object_iterator = m_value.object->find(key); } - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } - - if (first.m_object == this or last.m_object == this) - { - JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); - } - - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert( - pos.m_it.array_iterator, - first.m_it.array_iterator, - last.m_it.array_iterator); return result; } /*! - @brief inserts elements + @brief returns the number of occurrences of a key in a JSON object - Inserts elements from initializer list @a ilist before iterator @a pos. + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). - @param[in] pos iterator before which the content will be inserted; may be - the end() iterator - @param[in] ilist initializer list to insert the values from + @note This method always returns `0` when executed on a JSON type that is + not an object. - @throw type_error.309 if called on JSON values other than arrays; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if @a pos is not an iterator of *this; - example: `"iterator does not fit current value"` + @param[in] key key value of the element to count - @return iterator pointing to the first element inserted, or @a pos if - `ilist` is empty + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. - @complexity Linear in `ilist.size()` plus linear in the distance between - @a pos and end of the container. + @complexity Logarithmic in the size of the JSON object. - @liveexample{The example shows how `insert()` is used.,insert__ilist} + @liveexample{The example shows how `count()` is used.,count} @since version 1.0.0 */ - iterator insert(const_iterator pos, std::initializer_list ilist) + size_type count(typename object_t::key_type key) const { - // insert only works for arrays - if (not is_array()) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } + // return 0 for all nonobject types + return is_object() ? m_value.object->count(key) : 0; + } - // check if iterator pos fits to this JSON value - if (pos.m_object != this) - { - JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); - } + /// @} - // insert to array and return iterator - iterator result(this); - result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); - return result; - } + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ /*! - @brief inserts elements + @brief returns an iterator to the first element - Inserts elements from range `[first, last)`. + Returns an iterator to the first element. - @param[in] first begin of the range of elements to insert - @param[in] last end of the range of elements to insert + @image html range-begin-end.svg "Illustration from cppreference.com" - @throw type_error.309 if called on JSON values other than objects; example: - `"cannot use insert() with string"` - @throw invalid_iterator.202 if iterator @a first or @a last does does not - point to an object; example: `"iterators first and last must point to - objects"` - @throw invalid_iterator.210 if @a first and @a last do not belong to the - same JSON value; example: `"iterators do not fit"` + @return iterator to the first element - @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number - of elements to insert. + @complexity Constant. - @liveexample{The example shows how `insert()` is used.,insert__range_object} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. - @since version 3.0.0 - */ - void insert(const_iterator first, const_iterator last) - { - // insert only works for objects - if (not is_object()) - { - JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); - } + @liveexample{The following code shows an example for `begin()`.,begin} - // check if range iterators belong to the same JSON object - if (first.m_object != last.m_object) - { - JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); - } + @sa @ref cbegin() -- returns a const iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end - // passed iterators must belong to objects - if (not first.m_object->is_object() or not first.m_object->is_object()) - { - JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); - } + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } - m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); } /*! - @brief exchanges the values + @brief returns a const iterator to the first element - Exchanges the contents of the JSON value with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + Returns a const iterator to the first element. - @param[in,out] other JSON value to exchange the contents with + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element @complexity Constant. - @liveexample{The example below shows how JSON values can be swapped with - `swap()`.,swap__reference} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref end() -- returns an iterator to the end + @sa @ref cend() -- returns a const iterator to the end @since version 1.0.0 */ - void swap(reference other) noexcept ( - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value and - std::is_nothrow_move_constructible::value and - std::is_nothrow_move_assignable::value - ) + const_iterator cbegin() const noexcept { - std::swap(m_type, other.m_type); - std::swap(m_value, other.m_value); - assert_invariant(); + const_iterator result(this); + result.set_begin(); + return result; } /*! - @brief exchanges the values + @brief returns an iterator to one past the last element - Exchanges the contents of a JSON array with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + Returns an iterator to one past the last element. - @param[in,out] other array to exchange the contents with + @image html range-begin-end.svg "Illustration from cppreference.com" - @throw type_error.310 when JSON value is not an array; example: `"cannot - use swap() with string"` + @return iterator one past the last element @complexity Constant. - @liveexample{The example below shows how arrays can be swapped with - `swap()`.,swap__array_t} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa @ref cend() -- returns a const iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - void swap(array_t& other) + iterator end() noexcept { - // swap only works for arrays - if (is_array()) - { - std::swap(*(m_value.array), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + iterator result(this); + result.set_end(); + return result; } /*! - @brief exchanges the values + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } - Exchanges the contents of a JSON object with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. + /*! + @brief returns a const iterator to one past the last element - @param[in,out] other object to exchange the contents with + Returns a const iterator to one past the last element. - @throw type_error.310 when JSON value is not an object; example: - `"cannot use swap() with string"` + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element @complexity Constant. - @liveexample{The example below shows how objects can be swapped with - `swap()`.,swap__object_t} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa @ref end() -- returns an iterator to the end + @sa @ref begin() -- returns an iterator to the beginning + @sa @ref cbegin() -- returns a const iterator to the beginning @since version 1.0.0 */ - void swap(object_t& other) + const_iterator cend() const noexcept { - // swap only works for objects - if (is_object()) - { - std::swap(*(m_value.object), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + const_iterator result(this); + result.set_end(); + return result; } /*! - @brief exchanges the values + @brief returns an iterator to the reverse-beginning - Exchanges the contents of a JSON string with those of @a other. Does not - invoke any move, copy, or swap operations on individual elements. All - iterators and references remain valid. The past-the-end iterator is - invalidated. - - @param[in,out] other string to exchange the contents with + Returns an iterator to the reverse-beginning; that is, the last element. - @throw type_error.310 when JSON value is not a string; example: `"cannot - use swap() with boolean"` + @image html range-rbegin-rend.svg "Illustration from cppreference.com" @complexity Constant. - @liveexample{The example below shows how strings can be swapped with - `swap()`.,swap__string_t} + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa @ref crbegin() -- returns a const reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end @since version 1.0.0 */ - void swap(string_t& other) + reverse_iterator rbegin() noexcept { - // swap only works for strings - if (is_string()) - { - std::swap(*(m_value.string), other); - } - else - { - JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); - } + return reverse_iterator(end()); } - /// @} - - public: - ////////////////////////////////////////// - // lexicographical comparison operators // - ////////////////////////////////////////// - - /// @name lexicographical comparison operators - /// @{ + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } /*! - @brief comparison: equal + @brief returns an iterator to the reverse-end - Compares two JSON values for equality according to the following rules: - - Two JSON values are equal if (1) they are from the same type and (2) - their stored values are the same according to their respective - `operator==`. - - Integer and floating-point numbers are automatically converted before - comparison. Note than two NaN values are always treated as unequal. - - Two JSON null values are equal. + Returns an iterator to the reverse-end; that is, one before the first + element. - @note NaN values never compare equal to themselves or to other NaN values. + @image html range-rbegin-rend.svg "Illustration from cppreference.com" - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are equal + @complexity Constant. - @complexity Linear. + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. - @liveexample{The example demonstrates comparing several JSON - types.,operator__equal} + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa @ref crend() -- returns a const reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - friend bool operator==(const_reference lhs, const_reference rhs) noexcept + reverse_iterator rend() noexcept { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) - { - switch (lhs_type) - { - case value_t::array: - { - return *lhs.m_value.array == *rhs.m_value.array; - } - case value_t::object: - { - return *lhs.m_value.object == *rhs.m_value.object; - } - case value_t::null: - { - return true; - } - case value_t::string: - { - return *lhs.m_value.string == *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean == rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer == rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float == rhs.m_value.number_float; - } - default: - { - return false; - } - } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); - } - - return false; + return reverse_iterator(begin()); } /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) + @copydoc basic_json::crend() */ - template::value, int>::type = 0> - friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept + const_reverse_iterator rend() const noexcept { - return (lhs == basic_json(rhs)); + return crend(); } /*! - @brief comparison: equal - @copydoc operator==(const_reference, const_reference) + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 */ - template::value, int>::type = 0> - friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept + const_reverse_iterator crbegin() const noexcept { - return (basic_json(lhs) == rhs); + return const_reverse_iterator(cend()); } /*! - @brief comparison: not equal + @brief returns a const reverse iterator to one before the first - Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether the values @a lhs and @a rhs are not equal + @image html range-rbegin-rend.svg "Illustration from cppreference.com" - @complexity Linear. + @complexity Constant. - @liveexample{The example demonstrates comparing several JSON - types.,operator__notequal} + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](http://en.cppreference.com/w/cpp/concept/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa @ref rend() -- returns a reverse iterator to the end + @sa @ref rbegin() -- returns a reverse iterator to the beginning + @sa @ref crbegin() -- returns a const reverse iterator to the beginning @since version 1.0.0 */ - friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + const_reverse_iterator crend() const noexcept { - return not (lhs == rhs); + return const_reverse_iterator(cbegin()); } + public: /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @note The name of this function is not yet final and may change in the + future. */ - template::value, int>::type = 0> - friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept + static iteration_proxy iterator_wrapper(reference cont) { - return (lhs != basic_json(rhs)); + return iteration_proxy(cont); } /*! - @brief comparison: not equal - @copydoc operator!=(const_reference, const_reference) + @copydoc iterator_wrapper(reference) */ - template::value, int>::type = 0> - friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + static iteration_proxy iterator_wrapper(const_reference cont) { - return (basic_json(lhs) != rhs); + return iteration_proxy(cont); } - /*! - @brief comparison: less than + /// @} - Compares whether one JSON value @a lhs is less than another JSON value @a - rhs according to the following rules: - - If @a lhs and @a rhs have the same type, the values are compared using - the default `<` operator. - - Integer and floating-point numbers are automatically converted before - comparison - - In case @a lhs and @a rhs have different types, the values are ignored - and the order of the types is considered, see - @ref operator<(const value_t, const value_t). - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than @a rhs + ////////////// + // capacity // + ////////////// - @complexity Linear. + /// @name capacity + /// @{ - @liveexample{The example demonstrates comparing several JSON - types.,operator__less} + /*! + @brief checks whether the container is empty + + Checks if a JSON value has no elements. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @sa @ref size() -- returns the number of elements @since version 1.0.0 */ - friend bool operator<(const_reference lhs, const_reference rhs) noexcept + bool empty() const noexcept { - const auto lhs_type = lhs.type(); - const auto rhs_type = rhs.type(); - - if (lhs_type == rhs_type) + switch (m_type) { - switch (lhs_type) + case value_t::null: { - case value_t::array: - { - return (*lhs.m_value.array) < (*rhs.m_value.array); - } - case value_t::object: - { - return *lhs.m_value.object < *rhs.m_value.object; - } - case value_t::null: - { - return false; - } - case value_t::string: - { - return *lhs.m_value.string < *rhs.m_value.string; - } - case value_t::boolean: - { - return lhs.m_value.boolean < rhs.m_value.boolean; - } - case value_t::number_integer: - { - return lhs.m_value.number_integer < rhs.m_value.number_integer; - } - case value_t::number_unsigned: - { - return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; - } - case value_t::number_float: - { - return lhs.m_value.number_float < rhs.m_value.number_float; - } - default: - { - return false; - } + // null values are empty + return true; } - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; - } - else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) - { - return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); - } - else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) - { - return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; - } - // We only reach this line if we cannot compare values. In that case, - // we compare types. Note we have to call the operator explicitly, - // because MSVC has problems otherwise. - return operator<(lhs_type, rhs_type); - } + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs < basic_json(rhs)); - } + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } - /*! - @brief comparison: less than - @copydoc operator<(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) < rhs); + default: + { + // all other types are nonempty + return false; + } + } } /*! - @brief comparison: less than or equal + @brief returns the number of elements - Compares whether one JSON value @a lhs is less than or equal to another - JSON value by calculating `not (rhs < lhs)`. + Returns the number of elements in a JSON value. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is less than or equal to @a rhs + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + object | result of function object_t::size() + array | result of function array_t::size() - @complexity Linear. + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. - @liveexample{The example demonstrates comparing several JSON - types.,operator__greater} + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @sa @ref empty() -- checks whether the container is empty + @sa @ref max_size() -- returns the maximal number of elements @since version 1.0.0 */ - friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + size_type size() const noexcept { - return not (rhs < lhs); - } + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs <= basic_json(rhs)); - } + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } - /*! - @brief comparison: less than or equal - @copydoc operator<=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) <= rhs); + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + default: + { + // all other types have size 1 + return 1; + } + } } /*! - @brief comparison: greater than + @brief returns the maximum possible number of elements - Compares whether one JSON value @a lhs is greater than another - JSON value by calculating `not (lhs <= rhs)`. + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than to @a rhs + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` - @complexity Linear. + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. - @liveexample{The example demonstrates comparing several JSON - types.,operator__lessequal} + @requirement This function helps `basic_json` satisfying the + [Container](http://en.cppreference.com/w/cpp/concept/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @sa @ref size() -- returns the number of elements @since version 1.0.0 */ - friend bool operator>(const_reference lhs, const_reference rhs) noexcept + size_type max_size() const noexcept { - return not (lhs <= rhs); - } + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs > basic_json(rhs)); - } + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } - /*! - @brief comparison: greater than - @copydoc operator>(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) > rhs); + default: + { + // all other types have max_size() == size() + return size(); + } + } } + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + /*! - @brief comparison: greater than or equal + @brief clears the contents - Compares whether one JSON value @a lhs is greater than or equal to another - JSON value by calculating `not (lhs < rhs)`. + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called: - @param[in] lhs first JSON value to consider - @param[in] rhs second JSON value to consider - @return whether @a lhs is greater than or equal to @a rhs + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` - @complexity Linear. + @complexity Linear in the size of the JSON value. - @liveexample{The example demonstrates comparing several JSON - types.,operator__greaterequal} + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} @since version 1.0.0 */ - friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + void clear() noexcept { - return not (lhs < rhs); - } + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept - { - return (lhs >= basic_json(rhs)); - } + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } - /*! - @brief comparison: greater than or equal - @copydoc operator>=(const_reference, const_reference) - */ - template::value, int>::type = 0> - friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept - { - return (basic_json(lhs) >= rhs); - } + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } - /// @} + case value_t::boolean: + { + m_value.boolean = false; + break; + } - private: - ///////////////////// - // output adapters // - ///////////////////// + case value_t::string: + { + m_value.string->clear(); + break; + } - /// abstract output adapter interface - template - class output_adapter - { - public: - virtual void write_character(CharType c) = 0; - virtual void write_characters(const CharType* s, size_t length) = 0; - virtual ~output_adapter() {} + case value_t::array: + { + m_value.array->clear(); + break; + } - static std::shared_ptr> create(std::vector& vec) - { - return std::make_shared>(vec); - } + case value_t::object: + { + m_value.object->clear(); + break; + } - static std::shared_ptr> create(std::ostream& s) - { - return std::make_shared>(s); + default: + { + break; + } } + } - static std::shared_ptr> create(std::string& s) - { - return std::make_shared>(s); - } - }; + /*! + @brief add an object to an array - /// a type to simplify interfaces - template - using output_adapter_t = std::shared_ptr>; + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. - /// output adapter for byte vectors - template - class output_vector_adapter : public output_adapter - { - public: - output_vector_adapter(std::vector& vec) - : v(vec) - {} + @param[in] val the value to add to the JSON array - void write_character(CharType c) override - { - v.push_back(c); - } + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` - void write_characters(const CharType* s, size_t length) override - { - std::copy(s, s + length, std::back_inserter(v)); - } + @complexity Amortized constant. - private: - std::vector& v; - }; + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} - /// output adapter for output streams - template - class output_stream_adapter : public output_adapter + @since version 1.0.0 + */ + void push_back(basic_json&& val) { - public: - output_stream_adapter(std::basic_ostream& s) - : stream(s) - {} - - void write_character(CharType c) override + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) { - stream.put(c); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } - void write_characters(const CharType* s, size_t length) override + // transform null object into an array + if (is_null()) { - stream.write(s, static_cast(length)); + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); } - private: - std::basic_ostream& stream; - }; + // add element to array (move semantics) + m_value.array->push_back(std::move(val)); + // invalidate object + val.m_type = value_t::null; + } - /// output adapter for basic_string - template - class output_string_adapter : public output_adapter + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) { - public: - output_string_adapter(std::string& s) - : str(s) - {} + push_back(std::move(val)); + return *this; + } - void write_character(CharType c) override + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (not(is_null() or is_array())) { - str.push_back(c); + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); } - void write_characters(const CharType* s, size_t length) override + // transform null object into an array + if (is_null()) { - str.append(s, length); + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); } - private: - std::basic_string& str; - }; - + // add element to array + m_value.array->push_back(val); + } - /////////////////// - // serialization // - /////////////////// - - /// @name serialization - /// @{ - - private: /*! - @brief wrapper around the serialization functions + @brief add an object to an array + @copydoc push_back(basic_json&&) */ - class serializer + reference operator+=(const basic_json& val) { - public: - /*! - @param[in] s output stream to serialize to - @param[in] ichar indentation character to use - */ - serializer(output_adapter_t s, const char ichar) - : o(s), loc(std::localeconv()), - thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), - decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]), - indent_char(ichar), indent_string(512, indent_char) - {} + push_back(val); + return *this; + } - // delete because of pointer members - serializer(const serializer&) = delete; - serializer& operator=(const serializer&) = delete; + /*! + @brief add an object to an object - /*! - @brief internal implementation of the serialization function + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. - This function is called by the public member function dump and - organizes the serialization internally. The indentation level is - propagated as additional parameter. In case of arrays and objects, the - function is called recursively. + @param[in] val the value to add to the JSON object - - strings and object keys are escaped using `escape_string()` - - integer numbers are converted implicitly via `operator<<` - - floating-point numbers are converted to a string using `"%g"` format + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` - @param[in] val value to serialize - @param[in] pretty_print whether the output shall be pretty-printed - @param[in] indent_step the indent level - @param[in] current_indent the current indent level (only used internally) - */ - void dump(const basic_json& val, - const bool pretty_print, - const unsigned int indent_step, - const unsigned int current_indent = 0) - { - switch (val.m_type) - { - case value_t::object: - { - if (val.m_value.object->empty()) - { - o->write_characters("{}", 2); - return; - } + @complexity Logarithmic in the size of the container, O(log(`size()`)). - if (pretty_print) - { - o->write_characters("{\n", 2); + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (JSON_UNLIKELY(indent_string.size() < new_indent)) - { - indent_string.resize(indent_string.size() * 2, ' '); - } + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (not(is_null() or is_object())) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + type_name())); + } - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } - // last element - assert(i != val.m_value.object->cend()); - o->write_characters(indent_string.c_str(), new_indent); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\": ", 3); - dump(i->second, true, indent_step, new_indent); + // add element to array + m_value.object->insert(val); + } - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character('}'); - } - else - { - o->write_character('{'); + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } - // first n-1 elements - auto i = val.m_value.object->cbegin(); - for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) - { - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); - o->write_character(','); - } + /*! + @brief add an object to an object - // last element - assert(i != val.m_value.object->cend()); - o->write_character('\"'); - dump_escaped(i->first); - o->write_characters("\":", 2); - dump(i->second, false, indent_step, current_indent); + This function allows to use `push_back` with an initializer list. In case - o->write_character('}'); - } + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, - return; - } + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). - case value_t::array: - { - if (val.m_value.array->empty()) - { - o->write_characters("[]", 2); - return; - } + @param[in] init an initializer list - if (pretty_print) - { - o->write_characters("[\n", 2); + @complexity Linear in the size of the initializer list @a init. - // variable to hold indentation for recursive calls - const auto new_indent = current_indent + indent_step; - if (indent_string.size() < new_indent) - { - indent_string.resize(new_indent, ' '); - } + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - o->write_characters(indent_string.c_str(), new_indent); - dump(*i, true, indent_step, new_indent); - o->write_characters(",\n", 2); - } + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(std::initializer_list init) + { + if (is_object() and init.size() == 2 and init.begin()->is_string()) + { + const string_t key = *init.begin(); + push_back(typename object_t::value_type(key, *(init.begin() + 1))); + } + else + { + push_back(basic_json(init)); + } + } - // last element - assert(not val.m_value.array->empty()); - o->write_characters(indent_string.c_str(), new_indent); - dump(val.m_value.array->back(), true, indent_step, new_indent); + /*! + @brief add an object to an object + @copydoc push_back(std::initializer_list) + */ + reference operator+=(std::initializer_list init) + { + push_back(init); + return *this; + } - o->write_character('\n'); - o->write_characters(indent_string.c_str(), current_indent); - o->write_character(']'); - } - else - { - o->write_character('['); + /*! + @brief add an object to an array - // first n-1 elements - for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) - { - dump(*i, false, indent_step, current_indent); - o->write_character(','); - } + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. - // last element - assert(not val.m_value.array->empty()); - dump(val.m_value.array->back(), false, indent_step, current_indent); + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object - o->write_character(']'); - } + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` - return; - } + @complexity Amortized constant. - case value_t::string: - { - o->write_character('\"'); - dump_escaped(*val.m_value.string); - o->write_character('\"'); - return; - } + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} - case value_t::boolean: - { - if (val.m_value.boolean) - { - o->write_characters("true", 4); - } - else - { - o->write_characters("false", 5); - } - return; - } + @since version 2.0.8 + */ + template + void emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (not(is_null() or is_array())) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + type_name())); + } - case value_t::number_integer: - { - dump_integer(val.m_value.number_integer); - return; - } + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } - case value_t::number_unsigned: - { - dump_integer(val.m_value.number_unsigned); - return; - } + // add element to array (perfect forwarding) + m_value.array->emplace_back(std::forward(args)...); + } - case value_t::number_float: - { - dump_float(val.m_value.number_float); - return; - } + /*! + @brief add an object to an object if key does not exist - case value_t::discarded: - { - o->write_characters("", 11); - return; - } + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. - case value_t::null: - { - o->write_characters("null", 4); - return; - } - } - } + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object - private: - /*! - @brief calculates the extra space to escape a JSON string + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. - @param[in] s the string to escape - @return the number of characters required to escape string @a s + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` - @complexity Linear in the length of string @a s. - */ - static std::size_t extra_space(const string_t& s) noexcept - { - return std::accumulate(s.begin(), s.end(), size_t{}, - [](size_t res, typename string_t::value_type c) - { - switch (c) - { - case '"': - case '\\': - case '\b': - case '\f': - case '\n': - case '\r': - case '\t': - { - // from c (1 byte) to \x (2 bytes) - return res + 1; - } + @complexity Logarithmic in the size of the container, O(log(`size()`)). - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // from c (1 byte) to \uxxxx (6 bytes) - return res + 5; - } + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} - default: - { - return res; - } - } - }); + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (not(is_null() or is_object())) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + type_name())); } - /*! - @brief dump escaped string + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } - Escape a string by replacing certain special characters by a sequence - of an escape character (backslash) and another character and other - control characters by a sequence of "\u" followed by a four-digit hex - representation. The escaped string is written to output stream @a o. + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; - @param[in] s the string to escape + // return pair of iterator and boolean + return {it, res.second}; + } - @complexity Linear in the length of string @a s. - */ - void dump_escaped(const string_t& s) const + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (is_array()) { - const auto space = extra_space(s); - if (space == 0) + // check if iterator pos fits to this JSON value + if (pos.m_object != this) { - o->write_characters(s.c_str(), s.size()); - return; + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); } - // create a result string of necessary size - string_t result(s.size() + space, '\\'); - std::size_t pos = 0; + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, val); + return result; + } - for (const auto& c : s) - { - switch (c) - { - // quotation mark (0x22) - case '"': - { - result[pos + 1] = '"'; - pos += 2; - break; - } + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - // reverse solidus (0x5c) - case '\\': - { - // nothing to change - pos += 2; - break; - } + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } - // backspace (0x08) - case '\b': - { - result[pos + 1] = 'b'; - pos += 2; - break; - } + /*! + @brief inserts elements - // formfeed (0x0c) - case '\f': - { - result[pos + 1] = 'f'; - pos += 2; - break; - } + Inserts @a cnt copies of @a val before iterator @a pos. - // newline (0x0a) - case '\n': - { - result[pos + 1] = 'n'; - pos += 2; - break; - } + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` - // carriage return (0x0d) - case '\r': - { - result[pos + 1] = 'r'; - pos += 2; - break; - } + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - // horizontal tab (0x09) - case '\t': - { - result[pos + 1] = 't'; - pos += 2; - break; - } + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x0b: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - { - // convert a number 0..15 to its hex representation - // (0..f) - static const char hexify[16] = - { - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' - }; + @liveexample{The example shows how `insert()` is used.,insert__count} - // print character c as \uxxxx - for (const char m : - { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] - }) - { - result[++pos] = m; - } + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (is_array()) + { + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } + + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + return result; + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` - ++pos; - break; - } + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. - default: - { - // all other characters are added as-is - result[pos++] = c; - break; - } - } - } + @liveexample{The example shows how `insert()` is used.,insert__range} - assert(pos == s.size() + space); - o->write_characters(result.c_str(), result.size()); + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (not is_array()) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } - /*! - @brief dump an integer + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } - Dump a given integer to output stream @a o. Works internally with - @a number_buffer. + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } - @param[in] x integer number (signed or unsigned) to dump - @tparam NumberType either @a number_integer_t or @a number_unsigned_t - */ - template::value or - std::is_same::value, int> = 0> - void dump_integer(NumberType x) + if (first.m_object == this or last.m_object == this) { - // special case for "0" - if (x == 0) - { - o->write_character('0'); - return; - } + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container")); + } - const bool is_negative = x < 0; - size_t i = 0; + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert( + pos.m_it.array_iterator, + first.m_it.array_iterator, + last.m_it.array_iterator); + return result; + } - // spare 1 byte for '\0' - while (x != 0 and i < number_buffer.size() - 1) - { - const auto digit = std::labs(static_cast(x % 10)); - number_buffer[i++] = static_cast('0' + digit); - x /= 10; - } + /*! + @brief inserts elements - // make sure the number has been processed completely - assert(x == 0); + Inserts elements from initializer list @a ilist before iterator @a pos. - if (is_negative) - { - // make sure there is capacity for the '-' - assert(i < number_buffer.size() - 2); - number_buffer[i++] = '-'; - } + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from - std::reverse(number_buffer.begin(), number_buffer.begin() + i); - o->write_characters(number_buffer.data(), i); - } + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` - /*! - @brief dump a floating-point number + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty - Dump a given floating-point number to output stream @a o. Works - internally with @a number_buffer. + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. - @param[in] x floating-point number to dump - */ - void dump_float(number_float_t x) + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, std::initializer_list ilist) + { + // insert only works for arrays + if (not is_array()) { - // NaN / inf - if (not std::isfinite(x) or std::isnan(x)) - { - o->write_characters("null", 4); - return; - } + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); + } - // special case for 0.0 and -0.0 - if (x == 0) - { - if (std::signbit(x)) - { - o->write_characters("-0.0", 4); - } - else - { - o->write_characters("0.0", 3); - } - return; - } + // check if iterator pos fits to this JSON value + if (pos.m_object != this) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value")); + } - // get number of digits for a text -> float -> text round-trip - static constexpr auto d = std::numeric_limits::digits10; + // insert to array and return iterator + iterator result(this); + result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, ilist); + return result; + } - // the actual conversion - std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), - "%.*g", d, x); + /*! + @brief inserts elements - // negative value indicates an error - assert(len > 0); - // check if buffer was large enough - assert(static_cast(len) < number_buffer.size()); + Inserts elements from range `[first, last)`. - // erase thousands separator - if (thousands_sep != '\0') - { - const auto end = std::remove(number_buffer.begin(), - number_buffer.begin() + len, - thousands_sep); - std::fill(end, number_buffer.end(), '\0'); - assert((end - number_buffer.begin()) <= len); - len = (end - number_buffer.begin()); - } + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert - // convert decimal point to '.' - if (decimal_point != '\0' and decimal_point != '.') - { - for (auto& c : number_buffer) - { - if (c == decimal_point) - { - c = '.'; - break; - } - } - } + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` - o->write_characters(number_buffer.data(), static_cast(len)); + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. - // determine if need to append ".0" - const bool value_is_int_like = std::none_of(number_buffer.begin(), - number_buffer.begin() + len + 1, - [](char c) - { - return c == '.' or c == 'e'; - }); + @liveexample{The example shows how `insert()` is used.,insert__range_object} - if (value_is_int_like) - { - o->write_characters(".0", 2); - } + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (not is_object()) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + type_name())); } - private: - /// the output of the serializer - output_adapter_t o = nullptr; + // check if range iterators belong to the same JSON object + if (first.m_object != last.m_object) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit")); + } + + // passed iterators must belong to objects + if (not first.m_object->is_object() or not first.m_object->is_object()) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects")); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value and + std::is_nothrow_move_constructible::value and + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + assert_invariant(); + } + + /*! + @brief exchanges the values - /// a (hopefully) large enough character buffer - std::array number_buffer{{}}; + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - /// the locale - const std::lconv* loc = nullptr; - /// the locale's thousand separator character - const char thousands_sep = '\0'; - /// the locale's decimal point character - const char decimal_point = '\0'; + @param[in,out] other array to exchange the contents with - /// the indentation character - const char indent_char; + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` - /// the indentation string - string_t indent_string; - }; + @complexity Constant. - public: - /*! - @brief serialize to stream + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} - Serialize the given JSON value @a j to the output stream @a o. The JSON - value will be serialized using the @ref dump member function. + @since version 1.0.0 + */ + void swap(array_t& other) + { + // swap only works for arrays + if (is_array()) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } + } - - The indentation of the output can be controlled with the member variable - `width` of the output stream @a o. For instance, using the manipulator - `std::setw(4)` on @a o sets the indentation level to `4` and the - serialization result is the same as calling `dump(4)`. + /*! + @brief exchanges the values - - The indentation characrer can be controlled with the member variable - `fill` of the output stream @a o. For instance, the manipulator - `std::setfill('\\t')` sets indentation to use a tab character rather than - the default space character. + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. - @param[in,out] o stream to serialize to - @param[in] j JSON value to serialize + @param[in,out] other object to exchange the contents with - @return the stream @a o + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` - @complexity Linear. + @complexity Constant. - @liveexample{The example below shows the serialization with different - parameters to `width` to adjust the indentation level.,operator_serialize} + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} - @since version 1.0.0; indentaction character added in version 3.0.0 + @since version 1.0.0 */ - friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + void swap(object_t& other) { - // read width member and use it as indentation parameter if nonzero - const bool pretty_print = (o.width() > 0); - const auto indentation = (pretty_print ? o.width() : 0); - - // reset width to 0 for subsequent calls to this stream - o.width(0); - - // do the actual serialization - serializer s(output_adapter::create(o), o.fill()); - s.dump(j, pretty_print, static_cast(indentation)); - return o; + // swap only works for objects + if (is_object()) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } } /*! - @brief serialize to stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::ostream& operator<<(std::ostream&, const basic_json&) - instead; that is, replace calls like `j >> o;` with `o << j;`. + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 */ - JSON_DEPRECATED - friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + void swap(string_t& other) { - return o << j; + // swap only works for strings + if (is_string()) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + type_name())); + } } /// @} + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// - ///////////////////// - // deserialization // - ///////////////////// - - /// @name deserialization + /// @name lexicographical comparison operators /// @{ /*! - @brief deserialize from an array + @brief comparison: equal - This function reads from an array of 1-byte values. + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note than two NaN values are always treated as unequal. + - Two JSON null values are equal. - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** + @note NaN values never compare equal to themselves or to other NaN values. - @param[in] array array to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal - @return result of the deserialization + @complexity Linear. - @throw parse_error.101 if a parse error occurs; example: `""unexpected end - of input; expected string literal""` - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); - @note A UTF-8 byte order mark is silently ignored. + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return *lhs.m_value.array == *rhs.m_value.array; + } + case value_t::object: + { + return *lhs.m_value.object == *rhs.m_value.object; + } + case value_t::null: + { + return true; + } + case value_t::string: + { + return *lhs.m_value.string == *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean == rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer == rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float == rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } - @liveexample{The example below demonstrates the `parse()` function reading - from an array.,parse__array__parser_callback_t} + return false; + } - @since version 2.0.3 + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) */ - template - static basic_json parse(T (&array)[N], - const parser_callback_t cb = nullptr) + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, const ScalarType rhs) noexcept { - // delegate the call to the iterator-range parse overload - return parse(std::begin(array), std::end(array), cb); + return (lhs == basic_json(rhs)); } - template - static bool accept(T (&array)[N]) + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const ScalarType lhs, const_reference rhs) noexcept { - // delegate the call to the iterator-range accept overload - return accept(std::begin(array), std::end(array)); + return (basic_json(lhs) == rhs); } /*! - @brief deserialize from string literal - - @tparam CharT character/literal type with size of 1 byte - @param[in] s string literal to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization - - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @brief comparison: not equal - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. - @note A UTF-8 byte order mark is silently ignored. - @note String containers like `std::string` or @ref string_t can be parsed - with @ref parse(const ContiguousContainer&, const parser_callback_t) + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__string__parser_callback_t} + @complexity Linear. - @sa @ref parse(std::istream&, const parser_callback_t) for a version that - reads from an input stream + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} - @since version 1.0.0 (originally for @ref string_t) + @since version 1.0.0 */ - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static basic_json parse(const CharT s, - const parser_callback_t cb = nullptr) + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept { - return parser(detail::input_adapter_factory::create(s), cb).parse(true); + return not (lhs == rhs); } - template::value and - std::is_integral::type>::value and - sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> - static bool accept(const CharT s) + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, const ScalarType rhs) noexcept { - return parser(detail::input_adapter_factory::create(s)).accept(true); + return (lhs != basic_json(rhs)); } /*! - @brief deserialize from stream - - @param[in,out] i stream to read a serialized JSON value from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) != rhs); + } - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + /*! + @brief comparison: less than - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). - @note A UTF-8 byte order mark is silently ignored. + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs - @liveexample{The example below demonstrates the `parse()` function with - and without callback function.,parse__istream__parser_callback_t} + @complexity Linear. - @sa @ref parse(const CharT, const parser_callback_t) for a version - that reads from a string + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} @since version 1.0.0 */ - static basic_json parse(std::istream& i, - const parser_callback_t cb = nullptr) + friend bool operator<(const_reference lhs, const_reference rhs) noexcept { - return parser(detail::input_adapter_factory::create(i), cb).parse(true); - } + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); - static bool accept(std::istream& i) - { - return parser(detail::input_adapter_factory::create(i)).accept(true); + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + { + return (*lhs.m_value.array) < (*rhs.m_value.array); + } + case value_t::object: + { + return *lhs.m_value.object < *rhs.m_value.object; + } + case value_t::null: + { + return false; + } + case value_t::string: + { + return *lhs.m_value.string < *rhs.m_value.string; + } + case value_t::boolean: + { + return lhs.m_value.boolean < rhs.m_value.boolean; + } + case value_t::number_integer: + { + return lhs.m_value.number_integer < rhs.m_value.number_integer; + } + case value_t::number_unsigned: + { + return lhs.m_value.number_unsigned < rhs.m_value.number_unsigned; + } + case value_t::number_float: + { + return lhs.m_value.number_float < rhs.m_value.number_float; + } + default: + { + return false; + } + } + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer and rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned and rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); } /*! - @copydoc parse(std::istream&, const parser_callback_t) + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) */ - static basic_json parse(std::istream&& i, - const parser_callback_t cb = nullptr) + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, const ScalarType rhs) noexcept { - return parser(detail::input_adapter_factory::create(i), cb).parse(true); + return (lhs < basic_json(rhs)); } - static bool accept(std::istream&& i) + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const ScalarType lhs, const_reference rhs) noexcept { - return parser(detail::input_adapter_factory::create(i)).accept(true); + return (basic_json(lhs) < rhs); } /*! - @brief deserialize from an iterator range with contiguous storage - - This function reads from an iterator range of a container with contiguous - storage of 1-byte values. Compatible container types include - `std::vector`, `std::string`, `std::array`, `std::valarray`, and - `std::initializer_list`. Furthermore, C-style arrays can be used with - `std::begin()`/`std::end()`. User-defined containers can be used as long - as they implement random-access iterators and a contiguous storage. - - @pre The iterator range is contiguous. Violating this precondition yields - undefined behavior. **This precondition is enforced with an assertion.** - @pre Each element in the range has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with noncompliant iterators and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam IteratorType iterator of container with contiguous storage - @param[in] first begin of the range to parse (included) - @param[in] last end of the range to parse (excluded) - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) - - @return result of the deserialization + @brief comparison: less than or equal - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs - @note A UTF-8 byte order mark is silently ignored. + @complexity Linear. - @liveexample{The example below demonstrates the `parse()` function reading - from an iterator range.,parse__iteratortype__parser_callback_t} + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} - @since version 2.0.3 + @since version 1.0.0 */ - template::iterator_category>::value, int>::type = 0> - static basic_json parse(IteratorType first, IteratorType last, - const parser_callback_t cb = nullptr) + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept { - return parser(detail::input_adapter_factory::create(first, last), cb).parse(true); + return not (rhs < lhs); } - template::iterator_category>::value, int>::type = 0> - static bool accept(IteratorType first, IteratorType last) + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, const ScalarType rhs) noexcept { - return parser(detail::input_adapter_factory::create(first, last)).accept(true); + return (lhs <= basic_json(rhs)); } /*! - @brief deserialize from a container with contiguous storage - - This function reads from a container with contiguous storage of 1-byte - values. Compatible container types include `std::vector`, `std::string`, - `std::array`, and `std::initializer_list`. User-defined containers can be - used as long as they implement random-access iterators and a contiguous - storage. - - @pre The container storage is contiguous. Violating this precondition - yields undefined behavior. **This precondition is enforced with an - assertion.** - @pre Each element of the container has a size of 1 byte. Violating this - precondition yields undefined behavior. **This precondition is enforced - with a static assertion.** - - @warning There is no way to enforce all preconditions at compile-time. If - the function is called with a noncompliant container and with - assertions switched off, the behavior is undefined and will most - likely yield segmentation violation. - - @tparam ContiguousContainer container type with contiguous storage - @param[in] c container to read from - @param[in] cb a parser callback function of type @ref parser_callback_t - which is used to control the deserialization by filtering unwanted values - (optional) + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const ScalarType lhs, const_reference rhs) noexcept + { + return (basic_json(lhs) <= rhs); + } - @return result of the deserialization + /*! + @brief comparison: greater than - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. The complexity can be higher if the parser callback function - @a cb has a super-linear complexity. + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs - @note A UTF-8 byte order mark is silently ignored. + @complexity Linear. - @liveexample{The example below demonstrates the `parse()` function reading - from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} - @since version 2.0.3 + @since version 1.0.0 */ - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static basic_json parse(const ContiguousContainer& c, - const parser_callback_t cb = nullptr) + friend bool operator>(const_reference lhs, const_reference rhs) noexcept { - // delegate the call to the iterator-range parse overload - return parse(std::begin(c), std::end(c), cb); + return not (lhs <= rhs); } - template::value and - std::is_base_of< - std::random_access_iterator_tag, - typename std::iterator_traits()))>::iterator_category>::value - , int>::type = 0> - static bool accept(const ContiguousContainer& c) + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, const ScalarType rhs) noexcept { - // delegate the call to the iterator-range accept overload - return accept(std::begin(c), std::end(c)); + return (lhs > basic_json(rhs)); } /*! - @brief deserialize from stream - @deprecated This stream operator is deprecated and will be removed in a - future version of the library. Please use - @ref std::istream& operator>>(std::istream&, basic_json&) - instead; that is, replace calls like `j << i;` with `i >> j;`. + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) */ - JSON_DEPRECATED - friend std::istream& operator<<(basic_json& j, std::istream& i) + template::value, int>::type = 0> + friend bool operator>(const ScalarType lhs, const_reference rhs) noexcept { - j = parser(detail::input_adapter_factory::create(i)).parse(false); - return i; + return (basic_json(lhs) > rhs); } /*! - @brief deserialize from stream - - Deserializes an input stream to a JSON value. + @brief comparison: greater than or equal - @param[in,out] i input stream to read a serialized JSON value from - @param[in,out] j JSON value to write the deserialized input to + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. - @throw parse_error.101 in case of an unexpected token - @throw parse_error.102 if to_unicode fails or surrogate error - @throw parse_error.103 if to_unicode fails + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs - @complexity Linear in the length of the input. The parser is a predictive - LL(1) parser. + @complexity Linear. - @note A UTF-8 byte order mark is silently ignored. + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} - @liveexample{The example below shows how a JSON value is constructed by - reading a serialization from a stream.,operator_deserialize} + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return not (lhs < rhs); + } - @sa parse(std::istream&, const parser_callback_t) for a variant with a - parser callback function to filter values while parsing + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, const ScalarType rhs) noexcept + { + return (lhs >= basic_json(rhs)); + } - @since version 1.0.0 + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) */ - friend std::istream& operator>>(std::istream& i, basic_json& j) + template::value, int>::type = 0> + friend bool operator>=(const ScalarType lhs, const_reference rhs) noexcept { - j = parser(detail::input_adapter_factory::create(i)).parse(false); - return i; + return (basic_json(lhs) >= rhs); } /// @} - /////////////////////////// - // convenience functions // - /////////////////////////// + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ + private: /*! - @brief return the type as string + @brief wrapper around the serialization functions + */ + class serializer + { + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + */ + serializer(output_adapter_t s, const char ichar) + : o(s), loc(std::localeconv()), + thousands_sep(!loc->thousands_sep ? '\0' : loc->thousands_sep[0]), + decimal_point(!loc->decimal_point ? '\0' : loc->decimal_point[0]), + indent_char(ichar), indent_string(512, indent_char) + {} - Returns the type name as string to be used in error messages - usually to - indicate that a function was called on a wrong JSON type. + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; - @return basically a string representation of a the @a m_type member + /*! + @brief internal implementation of the serialization function - @complexity Constant. + This function is called by the public member function dump and + organizes the serialization internally. The indentation level is + propagated as additional parameter. In case of arrays and objects, the + function is called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const basic_json& val, + const bool pretty_print, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } - @liveexample{The following code exemplifies `type_name()` for all JSON - types.,type_name} + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\": ", 3); + dump(i->second, true, indent_step, new_indent); + o->write_characters(",\n", 2); + } - @since version 1.0.0, public since 2.1.0 - */ - std::string type_name() const - { - { - 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::discarded: - return "discarded"; - default: - return "number"; - } - } - } + // last element + assert(i != val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\": ", 3); + dump(i->second, true, indent_step, new_indent); + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); - private: - ////////////////////// - // member variables // - ////////////////////// + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\":", 2); + dump(i->second, false, indent_step, current_indent); + o->write_character(','); + } - /// the type of the current element - value_t m_type = value_t::null; + // last element + assert(i != val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first); + o->write_characters("\":", 2); + dump(i->second, false, indent_step, current_indent); - /// the value of the current element - json_value m_value = {}; + o->write_character('}'); + } - ////////////////////////////////////////// - // binary serialization/deserialization // - ////////////////////////////////////////// + return; + } - /// @name binary serialization/deserialization support - /// @{ + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } - /*! - @brief deserialization of CBOR and MessagePack values - */ - class binary_reader - { - public: - /*! - @brief create a binary reader + if (pretty_print) + { + o->write_characters("[\n", 2); - @param[in] adapter input adapter to read from - */ - explicit binary_reader(detail::input_adapter_t adapter) - : ia(adapter), is_little_endian(little_endianess()) - { - assert(ia); - } + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (indent_string.size() < new_indent) + { + indent_string.resize(new_indent, ' '); + } - /*! - @brief create a JSON value from CBOR input + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, indent_step, new_indent); + o->write_characters(",\n", 2); + } - @param[in] get_char whether a new character should be retrieved from - the input (true, default) or whether the last - read character should be considered instead + // last element + assert(not val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, indent_step, new_indent); - @return JSON value created from CBOR input + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read - */ - basic_json parse_cbor(const bool get_char = true) - { - switch (get_char ? get() : current) - { - // EOF - case std::char_traits::eof(): - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, indent_step, current_indent); + o->write_character(','); + } - // Integer 0x00..0x17 (0..23) - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - { - return static_cast(current); - } + // last element + assert(not val.m_value.array->empty()); + dump(val.m_value.array->back(), false, indent_step, current_indent); - case 0x18: // Unsigned integer (one-byte uint8_t follows) - { - return get_number(); - } + o->write_character(']'); + } - case 0x19: // Unsigned integer (two-byte uint16_t follows) - { - return get_number(); + return; } - case 0x1a: // Unsigned integer (four-byte uint32_t follows) + case value_t::string: { - return get_number(); + o->write_character('\"'); + dump_escaped(*val.m_value.string); + o->write_character('\"'); + return; } - case 0x1b: // Unsigned integer (eight-byte uint64_t follows) + case value_t::boolean: { - return get_number(); + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; } - // Negative integer -1-0x00..-1-0x17 (-1..-24) - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: + case value_t::number_integer: { - return static_cast(0x20 - 1 - current); + dump_integer(val.m_value.number_integer); + return; } - case 0x38: // Negative integer (one-byte uint8_t follows) + case value_t::number_unsigned: { - // must be uint8_t ! - return static_cast(-1) - get_number(); + dump_integer(val.m_value.number_unsigned); + return; } - case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + case value_t::number_float: { - return static_cast(-1) - get_number(); + dump_float(val.m_value.number_float); + return; } - case 0x3a: // Negative integer -1-n (four-byte uint32_t follows) + case value_t::discarded: { - return static_cast(-1) - get_number(); + o->write_characters("", 11); + return; } - case 0x3b: // Negative integer -1-n (eight-byte uint64_t follows) + case value_t::null: { - return static_cast(-1) - static_cast(get_number()); + o->write_characters("null", 4); + return; } + } + } - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - case 0x7f: // UTF-8 string (indefinite length) - { - return get_cbor_string(); - } - - // array (0x00..0x17 data items follow) - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) + private: + /*! + @brief calculates the extra space to escape a JSON string + + @param[in] s the string to escape + @return the number of characters required to escape string @a s + + @complexity Linear in the length of string @a s. + */ + static std::size_t extra_space(const string_t& s) noexcept + { + return std::accumulate(s.begin(), s.end(), size_t{}, + [](size_t res, typename string_t::value_type c) + { + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': { - result.push_back(parse_cbor()); + // from c (1 byte) to \x (2 bytes) + return res + 1; } - return result; - } - case 0x98: // array (one-byte uint8_t for n follows) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x0b: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: { - result.push_back(parse_cbor()); + // from c (1 byte) to \uxxxx (6 bytes) + return res + 5; } - return result; - } - case 0x99: // array (two-byte uint16_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + default: { - result.push_back(parse_cbor()); + return res; } - return result; } + }); + } - case 0x9a: // array (four-byte uint32_t for n follow) + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence + of an escape character (backslash) and another character and other + control characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s) const + { + const auto space = extra_space(s); + if (space == 0) + { + o->write_characters(s.c_str(), s.size()); + return; + } + + // create a result string of necessary size + string_t result(s.size() + space, '\\'); + std::size_t pos = 0; + + for (const auto& c : s) + { + switch (c) { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + // quotation mark (0x22) + case '"': { - result.push_back(parse_cbor()); + result[pos + 1] = '"'; + pos += 2; + break; } - return result; - } - case 0x9b: // array (eight-byte uint64_t for n follow) - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + // reverse solidus (0x5c) + case '\\': { - result.push_back(parse_cbor()); + // nothing to change + pos += 2; + break; } - return result; - } - case 0x9f: // array (indefinite length) - { - basic_json result = value_t::array; - while (get() != 0xff) + // backspace (0x08) + case '\b': { - result.push_back(parse_cbor(false)); + result[pos + 1] = 'b'; + pos += 2; + break; } - return result; - } - // map (0x00..0x17 pairs of data items follow) - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x1f); - for (size_t i = 0; i < len; ++i) + // formfeed (0x0c) + case '\f': { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); + result[pos + 1] = 'f'; + pos += 2; + break; } - return result; - } - case 0xb8: // map (one-byte uint8_t for n follows) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + // newline (0x0a) + case '\n': { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); + result[pos + 1] = 'n'; + pos += 2; + break; } - return result; - } - case 0xb9: // map (two-byte uint16_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + // carriage return (0x0d) + case '\r': { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); + result[pos + 1] = 'r'; + pos += 2; + break; } - return result; - } - case 0xba: // map (four-byte uint32_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + // horizontal tab (0x09) + case '\t': { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); + result[pos + 1] = 't'; + pos += 2; + break; } - return result; - } - case 0xbb: // map (eight-byte uint64_t for n follow) - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x0b: + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: { - get(); - auto key = get_cbor_string(); - result[key] = parse_cbor(); + // convert a number 0..15 to its hex representation + // (0..f) + static const char hexify[16] = + { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + // print character c as \uxxxx + for (const char m : + { 'u', '0', '0', hexify[c >> 4], hexify[c & 0x0f] + }) + { + result[++pos] = m; + } + + ++pos; + break; } - return result; - } - case 0xbf: // map (indefinite length) - { - basic_json result = value_t::object; - while (get() != 0xff) + default: { - auto key = get_cbor_string(); - result[key] = parse_cbor(); + // all other characters are added as-is + result[pos++] = c; + break; } - return result; } + } - case 0xf4: // false - { - return false; - } + assert(pos == s.size() + space); + o->write_characters(result.c_str(), result.size()); + } - case 0xf5: // true - { - return true; - } + /*! + @brief dump an integer - case 0xf6: // null - { - return value_t::null; - } + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. - case 0xf9: // Half-Precision Float (two-byte IEEE 754) - { - const int byte1 = get(); - check_eof(); - const int byte2 = get(); - check_eof(); + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template::value or + std::is_same::value, int> = 0> + void dump_integer(NumberType x) + { + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } - // code from RFC 7049, Appendix D, Figure 3: - // As half-precision floating-point numbers were only added - // to IEEE 754 in 2008, today's programming platforms often - // still only have limited support for them. It is very - // easy to include at least decoding support for them even - // without such support. An example of a small decoder for - // half-precision floating-point numbers in the C language - // is shown in Fig. 3. - const int half = (byte1 << 8) + byte2; - const int exp = (half >> 10) & 0x1f; - const int mant = half & 0x3ff; - double val; - if (exp == 0) - { - val = std::ldexp(mant, -24); - } - else if (exp != 31) - { - val = std::ldexp(mant + 1024, exp - 25); - } - else - { - val = (mant == 0) - ? std::numeric_limits::infinity() - : std::numeric_limits::quiet_NaN(); - } - return (half & 0x8000) != 0 ? -val : val; - } + const bool is_negative = x < 0; + size_t i = 0; - case 0xfa: // Single-Precision Float (four-byte IEEE 754) - { - return get_number(); - } + // spare 1 byte for '\0' + while (x != 0 and i < number_buffer.size() - 1) + { + const auto digit = std::labs(static_cast(x % 10)); + number_buffer[i++] = static_cast('0' + digit); + x /= 10; + } - case 0xfb: // Double-Precision Float (eight-byte IEEE 754) - { - return get_number(); - } + // make sure the number has been processed completely + assert(x == 0); - default: // anything else (0xFF is handled inside the other types) - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading CBOR; last byte: 0x" + ss.str())); - } + if (is_negative) + { + // make sure there is capacity for the '-' + assert(i < number_buffer.size() - 2); + number_buffer[i++] = '-'; } + + std::reverse(number_buffer.begin(), number_buffer.begin() + i); + o->write_characters(number_buffer.data(), i); } /*! - @brief create a JSON value from MessagePack input + @brief dump a floating-point number - @return JSON value created from MessagePack input + Dump a given floating-point number to output stream @a o. Works + internally with @a number_buffer. - @throw parse_error.110 if input ended unexpectedly - @throw parse_error.112 if unsupported byte was read + @param[in] x floating-point number to dump */ - basic_json parse_msgpack() + void dump_float(number_float_t x) { - switch (get()) + // NaN / inf + if (not std::isfinite(x) or std::isnan(x)) { - // EOF - case std::char_traits::eof(): + o->write_characters("null", 4); + return; + } + + // special case for 0.0 and -0.0 + if (x == 0) + { + if (std::signbit(x)) { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); + o->write_characters("-0.0", 4); } - - // positive fixint - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - case 0x06: - case 0x07: - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - case 0x10: - case 0x11: - case 0x12: - case 0x13: - case 0x14: - case 0x15: - case 0x16: - case 0x17: - case 0x18: - case 0x19: - case 0x1a: - case 0x1b: - case 0x1c: - case 0x1d: - case 0x1e: - case 0x1f: - case 0x20: - case 0x21: - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0x28: - case 0x29: - case 0x2a: - case 0x2b: - case 0x2c: - case 0x2d: - case 0x2e: - case 0x2f: - case 0x30: - case 0x31: - case 0x32: - case 0x33: - case 0x34: - case 0x35: - case 0x36: - case 0x37: - case 0x38: - case 0x39: - case 0x3a: - case 0x3b: - case 0x3c: - case 0x3d: - case 0x3e: - case 0x3f: - case 0x40: - case 0x41: - case 0x42: - case 0x43: - case 0x44: - case 0x45: - case 0x46: - case 0x47: - case 0x48: - case 0x49: - case 0x4a: - case 0x4b: - case 0x4c: - case 0x4d: - case 0x4e: - case 0x4f: - case 0x50: - case 0x51: - case 0x52: - case 0x53: - case 0x54: - case 0x55: - case 0x56: - case 0x57: - case 0x58: - case 0x59: - case 0x5a: - case 0x5b: - case 0x5c: - case 0x5d: - case 0x5e: - case 0x5f: - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - case 0x78: - case 0x79: - case 0x7a: - case 0x7b: - case 0x7c: - case 0x7d: - case 0x7e: - case 0x7f: + else { - return static_cast(current); - } - - // fixmap - case 0x80: - case 0x81: - case 0x82: - case 0x83: - case 0x84: - case 0x85: - case 0x86: - case 0x87: - case 0x88: - case 0x89: - case 0x8a: - case 0x8b: - case 0x8c: - case 0x8d: - case 0x8e: - case 0x8f: - { - basic_json result = value_t::object; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; + o->write_characters("0.0", 3); } + return; + } + + // get number of digits for a text -> float -> text round-trip + static constexpr auto d = std::numeric_limits::digits10; + + // the actual conversion + std::ptrdiff_t len = snprintf(number_buffer.data(), number_buffer.size(), + "%.*g", d, x); + + // negative value indicates an error + assert(len > 0); + // check if buffer was large enough + assert(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + const auto end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, + thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + assert((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } - // fixarray - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - { - basic_json result = value_t::array; - const auto len = static_cast(current & 0x0f); - for (size_t i = 0; i < len; ++i) + // convert decimal point to '.' + if (decimal_point != '\0' and decimal_point != '.') + { + for (auto& c : number_buffer) + { + if (c == decimal_point) { - result.push_back(parse_msgpack()); + c = '.'; + break; } - return result; } + } - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - return get_msgpack_string(); - } - - case 0xc0: // nil - { - return value_t::null; - } - - case 0xc2: // false - { - return false; - } + o->write_characters(number_buffer.data(), static_cast(len)); - case 0xc3: // true - { - return true; - } + // determine if need to append ".0" + const bool value_is_int_like = std::none_of(number_buffer.begin(), + number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' or c == 'e'; + }); - case 0xca: // float 32 - { - return get_number(); - } + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } - case 0xcb: // float 64 - { - return get_number(); - } + private: + /// the output of the serializer + output_adapter_t o = nullptr; - case 0xcc: // uint 8 - { - return get_number(); - } + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; - case 0xcd: // uint 16 - { - return get_number(); - } + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// the indentation character + const char indent_char; + + /// the indentation string + string_t indent_string; + }; + + public: + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. - case 0xce: // uint 32 - { - return get_number(); - } + - The indentation characrer can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. - case 0xcf: // uint 64 - { - return get_number(); - } + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize - case 0xd0: // int 8 - { - return get_number(); - } + @return the stream @a o - case 0xd1: // int 16 - { - return get_number(); - } + @complexity Linear. - case 0xd2: // int 32 - { - return get_number(); - } + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} - case 0xd3: // int 64 - { - return get_number(); - } + @since version 1.0.0; indentaction character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = (o.width() > 0); + const auto indentation = (pretty_print ? o.width() : 0); - case 0xd9: // str 8 - case 0xda: // str 16 - case 0xdb: // str 32 - { - return get_msgpack_string(); - } + // reset width to 0 for subsequent calls to this stream + o.width(0); - case 0xdc: // array 16 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } + // do the actual serialization + serializer s(detail::output_adapter_factory::create(o), o.fill()); + s.dump(j, pretty_print, static_cast(indentation)); + return o; + } - case 0xdd: // array 32 - { - basic_json result = value_t::array; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - result.push_back(parse_msgpack()); - } - return result; - } + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in a + future version of the library. Please use + @ref std::ostream& operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + */ + JSON_DEPRECATED + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } - case 0xde: // map 16 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } + /// @} - case 0xdf: // map 32 - { - basic_json result = value_t::object; - const auto len = static_cast(get_number()); - for (size_t i = 0; i < len; ++i) - { - get(); - auto key = get_msgpack_string(); - result[key] = parse_msgpack(); - } - return result; - } - // positive fixint - case 0xe0: - case 0xe1: - case 0xe2: - case 0xe3: - case 0xe4: - case 0xe5: - case 0xe6: - case 0xe7: - case 0xe8: - case 0xe9: - case 0xea: - case 0xeb: - case 0xec: - case 0xed: - case 0xee: - case 0xef: - case 0xf0: - case 0xf1: - case 0xf2: - case 0xf3: - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - case 0xf8: - case 0xf9: - case 0xfa: - case 0xfb: - case 0xfc: - case 0xfd: - case 0xfe: - case 0xff: - { - return static_cast(current); - } + ///////////////////// + // deserialization // + ///////////////////// - default: // anything else - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(112, chars_read, "error reading MessagePack; last byte: 0x" + ss.str())); - } - } - } + /// @name deserialization + /// @{ - /*! - @brief determine system byte order + /*! + @brief deserialize from an array - @return true iff system's byte order is little endian + This function reads from an array of 1-byte values. - @note from http://stackoverflow.com/a/1001328/266378 - */ - static bool little_endianess() noexcept - { - int num = 1; - return (*reinterpret_cast(&num) == 1); - } + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - private: - /*! - @brief get next character from the input + @param[in] array array to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - This function provides the interface to the used input adapter. It does - not throw in case the input reached EOF, but returns - `std::char_traits::eof()` in that case. + @return result of the deserialization - @return character read from the input - */ - int get() - { - ++chars_read; - return (current = ia->get_character()); - } + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - /* - @brief read a number from the input + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - @tparam NumberType the type of the number + @note A UTF-8 byte order mark is silently ignored. - @return number of type @a NumberType + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. + @since version 2.0.3 + */ + template + static basic_json parse(T (&array)[N], + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(array), std::end(array), cb); + } - @throw parse_error.110 if input has less than `sizeof(NumberType)` - bytes - */ - template - NumberType get_number() - { - // step 1: read input into array with system's byte order - std::array vec; - for (size_t i = 0; i < sizeof(NumberType); ++i) - { - get(); - check_eof(); + template + static bool accept(T (&array)[N]) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(array), std::end(array)); + } - // reverse byte order prior to conversion if necessary - if (is_little_endian) - { - vec[sizeof(NumberType) - i - 1] = static_cast(current); - } - else - { - vec[i] = static_cast(current); // LCOV_EXCL_LINE - } - } + /*! + @brief deserialize from string literal - // step 2: convert array into number of type T and return - NumberType result; - std::memcpy(&result, vec.data(), sizeof(NumberType)); - return result; - } + @tparam CharT character/literal type with size of 1 byte + @param[in] s string literal to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - /*! - @brief create a string by reading characters from the input + @return result of the deserialization - @param[in] len number of bytes to read + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - @note We can not reserve @a len bytes for the result, because @a len - may be too large. Usually, @ref check_eof() detects the end of - the input before we run out of string memory. + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - @return string created by reading @a len bytes + @note A UTF-8 byte order mark is silently ignored. + @note String containers like `std::string` or @ref string_t can be parsed + with @ref parse(const ContiguousContainer&, const parser_callback_t) - @throw parse_error.110 if input has less than @a len bytes - */ - std::string get_string(const size_t len) - { - std::string result; - for (size_t i = 0; i < len; ++i) - { - get(); - check_eof(); - result.append(1, static_cast(current)); - } - return result; - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} - /*! - @brief reads a CBOR string + @sa @ref parse(std::istream&, const parser_callback_t) for a version that + reads from an input stream - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. - Additionally, CBOR's strings with indefinite lengths are supported. + @since version 1.0.0 (originally for @ref string_t) + */ + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static basic_json parse(const CharT s, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(s), cb).parse(true); + } - @return string + template::value and + std::is_integral::type>::value and + sizeof(typename std::remove_pointer::type) == 1, int>::type = 0> + static bool accept(const CharT s) + { + return parser(detail::input_adapter_factory::create(s)).accept(true); + } - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_cbor_string() - { - check_eof(); + /*! + @brief deserialize from stream - switch (current) - { - // UTF-8 string (0x00..0x17 bytes follow) - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x66: - case 0x67: - case 0x68: - case 0x69: - case 0x6a: - case 0x6b: - case 0x6c: - case 0x6d: - case 0x6e: - case 0x6f: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x76: - case 0x77: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } + @param[in,out] i stream to read a serialized JSON value from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case 0x78: // UTF-8 string (one-byte uint8_t for n follows) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @return result of the deserialization - case 0x79: // UTF-8 string (two-byte uint16_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - case 0x7a: // UTF-8 string (four-byte uint32_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - case 0x7b: // UTF-8 string (eight-byte uint64_t for n follow) - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @note A UTF-8 byte order mark is silently ignored. - case 0x7f: // UTF-8 string (indefinite length) - { - std::string result; - while (get() != 0xff) - { - check_eof(); - result.append(1, static_cast(current)); - } - return result; - } + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a CBOR string; last byte: 0x" + ss.str())); - } - } - } + @sa @ref parse(const CharT, const parser_callback_t) for a version + that reads from a string - /*! - @brief reads a MessagePack string + @since version 1.0.0 + */ + static basic_json parse(std::istream& i, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(i), cb).parse(true); + } - This function first reads starting bytes to determine the expected - string length and then copies this number of bytes into a string. + static bool accept(std::istream& i) + { + return parser(detail::input_adapter_factory::create(i)).accept(true); + } - @return string + /*! + @copydoc parse(std::istream&, const parser_callback_t) + */ + static basic_json parse(std::istream&& i, + const parser_callback_t cb = nullptr) + { + return parser(detail::input_adapter_factory::create(i), cb).parse(true); + } - @throw parse_error.110 if input ended - @throw parse_error.113 if an unexpected byte is read - */ - std::string get_msgpack_string() - { - check_eof(); + static bool accept(std::istream&& i) + { + return parser(detail::input_adapter_factory::create(i)).accept(true); + } - switch (current) - { - // fixstr - case 0xa0: - case 0xa1: - case 0xa2: - case 0xa3: - case 0xa4: - case 0xa5: - case 0xa6: - case 0xa7: - case 0xa8: - case 0xa9: - case 0xaa: - case 0xab: - case 0xac: - case 0xad: - case 0xae: - case 0xaf: - case 0xb0: - case 0xb1: - case 0xb2: - case 0xb3: - case 0xb4: - case 0xb5: - case 0xb6: - case 0xb7: - case 0xb8: - case 0xb9: - case 0xba: - case 0xbb: - case 0xbc: - case 0xbd: - case 0xbe: - case 0xbf: - { - const auto len = static_cast(current & 0x1f); - return get_string(len); - } + /*! + @brief deserialize from an iterator range with contiguous storage - case 0xd9: // str 8 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + This function reads from an iterator range of a container with contiguous + storage of 1-byte values. Compatible container types include + `std::vector`, `std::string`, `std::array`, `std::valarray`, and + `std::initializer_list`. Furthermore, C-style arrays can be used with + `std::begin()`/`std::end()`. User-defined containers can be used as long + as they implement random-access iterators and a contiguous storage. - case 0xda: // str 16 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @pre The iterator range is contiguous. Violating this precondition yields + undefined behavior. **This precondition is enforced with an assertion.** + @pre Each element in the range has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - case 0xdb: // str 32 - { - const auto len = static_cast(get_number()); - return get_string(len); - } + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with noncompliant iterators and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - default: - { - std::stringstream ss; - ss << std::setw(2) << std::setfill('0') << std::hex << current; - JSON_THROW(parse_error::create(113, chars_read, "expected a MessagePack string; last byte: 0x" + ss.str())); - } - } - } + @tparam IteratorType iterator of container with contiguous storage + @param[in] first begin of the range to parse (included) + @param[in] last end of the range to parse (excluded) + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - /*! - @brief check if input ended - @throw parse_error.110 if input ended - */ - void check_eof() const - { - if (JSON_UNLIKELY(current == std::char_traits::eof())) - { - JSON_THROW(parse_error::create(110, chars_read, "unexpected end of input")); - } - } + @return result of the deserialization - private: - /// input adapter - detail::input_adapter_t ia = nullptr; + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - /// the current character - int current = std::char_traits::eof(); + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. - /// the number of characters read - size_t chars_read = 0; + @note A UTF-8 byte order mark is silently ignored. - /// whether we can assume little endianess - const bool is_little_endian = true; - }; + @liveexample{The example below demonstrates the `parse()` function reading + from an iterator range.,parse__iteratortype__parser_callback_t} - /*! - @brief serialization to CBOR and MessagePack values + @since version 2.0.3 */ - class binary_writer + template::iterator_category>::value, int>::type = 0> + static basic_json parse(IteratorType first, IteratorType last, + const parser_callback_t cb = nullptr) { - public: - /*! - @brief create a binary writer + return parser(detail::input_adapter_factory::create(first, last), cb).parse(true); + } - @param[in] adapter output adapter to write to - */ - explicit binary_writer(output_adapter_t adapter) - : is_little_endian(binary_reader::little_endianess()), oa(adapter) - { - assert(oa); - } + template::iterator_category>::value, int>::type = 0> + static bool accept(IteratorType first, IteratorType last) + { + return parser(detail::input_adapter_factory::create(first, last)).accept(true); + } - /*! - @brief[in] j JSON value to serialize - */ - void write_cbor(const basic_json& j) - { - switch (j.type()) - { - case value_t::null: - { - oa->write_character(0xf6); - break; - } + /*! + @brief deserialize from a container with contiguous storage - case value_t::boolean: - { - oa->write_character(j.m_value.boolean ? 0xf5 : 0xf4); - break; - } + This function reads from a container with contiguous storage of 1-byte + values. Compatible container types include `std::vector`, `std::string`, + `std::array`, and `std::initializer_list`. User-defined containers can be + used as long as they implement random-access iterators and a contiguous + storage. - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // CBOR does not differentiate between positive signed - // integers and unsigned integers. Therefore, we used the - // code from the value_t::number_unsigned case here. - if (j.m_value.number_integer <= 0x17) - { - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_integer)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - // The conversions below encode the sign in the first - // byte, and the value is converted to a positive number. - const auto positive_number = -1 - j.m_value.number_integer; - if (j.m_value.number_integer >= -24) - { - write_number(static_cast(0x20 + positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x38); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x39); - write_number(static_cast(positive_number)); - } - else if (positive_number <= (std::numeric_limits::max)()) - { - oa->write_character(0x3a); - write_number(static_cast(positive_number)); - } - else - { - oa->write_character(0x3b); - write_number(static_cast(positive_number)); - } - } - break; - } + @pre The container storage is contiguous. Violating this precondition + yields undefined behavior. **This precondition is enforced with an + assertion.** + @pre Each element of the container has a size of 1 byte. Violating this + precondition yields undefined behavior. **This precondition is enforced + with a static assertion.** - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned <= 0x17) - { - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x18); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x19); - write_number(static_cast(j.m_value.number_unsigned)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - oa->write_character(0x1a); - write_number(static_cast(j.m_value.number_unsigned)); - } - else - { - oa->write_character(0x1b); - write_number(static_cast(j.m_value.number_unsigned)); - } - break; - } + @warning There is no way to enforce all preconditions at compile-time. If + the function is called with a noncompliant container and with + assertions switched off, the behavior is undefined and will most + likely yield segmentation violation. - case value_t::number_float: - { - // Double-Precision Float - oa->write_character(0xfb); - write_number(j.m_value.number_float); - break; - } + @tparam ContiguousContainer container type with contiguous storage + @param[in] c container to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) - case value_t::string: - { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 0x17) - { - write_number(static_cast(0x60 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x78); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x79); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x7a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x7b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + @return result of the deserialization + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 + */ + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static basic_json parse(const ContiguousContainer& c, + const parser_callback_t cb = nullptr) + { + // delegate the call to the iterator-range parse overload + return parse(std::begin(c), std::end(c), cb); + } - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; - } + template::value and + std::is_base_of< + std::random_access_iterator_tag, + typename std::iterator_traits()))>::iterator_category>::value + , int>::type = 0> + static bool accept(const ContiguousContainer& c) + { + // delegate the call to the iterator-range accept overload + return accept(std::begin(c), std::end(c)); + } - case value_t::array: - { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 0x17) - { - write_number(static_cast(0x80 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0x98); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0x99); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0x9a); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0x9b); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in a + future version of the library. Please use + @ref std::istream& operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + */ + JSON_DEPRECATED + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + j = parser(detail::input_adapter_factory::create(i)).parse(false); + return i; + } - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_cbor(el); - } - break; - } + /*! + @brief deserialize from stream - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 0x17) - { - write_number(static_cast(0xa0 + N)); - } - else if (N <= 0xff) - { - oa->write_character(0xb8); - write_number(static_cast(N)); - } - else if (N <= 0xffff) - { - oa->write_character(0xb9); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - oa->write_character(0xba); - write_number(static_cast(N)); - } - // LCOV_EXCL_START - else if (N <= 0xffffffffffffffff) - { - oa->write_character(0xbb); - write_number(static_cast(N)); - } - // LCOV_EXCL_STOP + Deserializes an input stream to a JSON value. - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_cbor(el.first); - write_cbor(el.second); - } - break; - } + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to - default: - { - break; - } - } - } + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails - /*! - @brief[in] j JSON value to serialize - */ - void write_msgpack(const basic_json& j) - { - switch (j.type()) - { - case value_t::null: - { - // nil - oa->write_character(0xc0); - break; - } + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. - case value_t::boolean: - { - // true and false - oa->write_character(j.m_value.boolean ? 0xc3 : 0xc2); - break; - } + @note A UTF-8 byte order mark is silently ignored. - case value_t::number_integer: - { - if (j.m_value.number_integer >= 0) - { - // MessagePack does not differentiate between positive - // signed integers and unsigned integers. Therefore, we - // used the code from the value_t::number_unsigned case - // here. - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - } - else - { - if (j.m_value.number_integer >= -32) - { - // negative fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 8 - oa->write_character(0xd0); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 16 - oa->write_character(0xd1); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 32 - oa->write_character(0xd2); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_integer >= (std::numeric_limits::min)() and j.m_value.number_integer <= (std::numeric_limits::max)()) - { - // int 64 - oa->write_character(0xd3); - write_number(static_cast(j.m_value.number_integer)); - } - } - break; - } + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} - case value_t::number_unsigned: - { - if (j.m_value.number_unsigned < 128) - { - // positive fixnum - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 8 - oa->write_character(0xcc); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 16 - oa->write_character(0xcd); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 32 - oa->write_character(0xce); - write_number(static_cast(j.m_value.number_integer)); - } - else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) - { - // uint 64 - oa->write_character(0xcf); - write_number(static_cast(j.m_value.number_integer)); - } - break; - } + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing - case value_t::number_float: - { - // float 64 - oa->write_character(0xcb); - write_number(j.m_value.number_float); - break; - } + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + j = parser(detail::input_adapter_factory::create(i)).parse(false); + return i; + } - case value_t::string: - { - // step 1: write control byte and the string length - const auto N = j.m_value.string->size(); - if (N <= 31) - { - // fixstr - write_number(static_cast(0xa0 | N)); - } - else if (N <= 255) - { - // str 8 - oa->write_character(0xd9); - write_number(static_cast(N)); - } - else if (N <= 65535) - { - // str 16 - oa->write_character(0xda); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // str 32 - oa->write_character(0xdb); - write_number(static_cast(N)); - } + /// @} - // step 2: write the string - oa->write_characters(reinterpret_cast(j.m_value.string->c_str()), - j.m_value.string->size()); - break; - } + /////////////////////////// + // convenience functions // + /////////////////////////// - case value_t::array: - { - // step 1: write control byte and the array size - const auto N = j.m_value.array->size(); - if (N <= 15) - { - // fixarray - write_number(static_cast(0x90 | N)); - } - else if (N <= 0xffff) - { - // array 16 - oa->write_character(0xdc); - write_number(static_cast(N)); - } - else if (N <= 0xffffffff) - { - // array 32 - oa->write_character(0xdd); - write_number(static_cast(N)); - } + /*! + @brief return the type as string - // step 2: write each element - for (const auto& el : *j.m_value.array) - { - write_msgpack(el); - } - break; - } + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. - case value_t::object: - { - // step 1: write control byte and the object size - const auto N = j.m_value.object->size(); - if (N <= 15) - { - // fixmap - write_number(static_cast(0x80 | (N & 0xf))); - } - else if (N <= 65535) - { - // map 16 - oa->write_character(0xde); - write_number(static_cast(N)); - } - else if (N <= 4294967295) - { - // map 32 - oa->write_character(0xdf); - write_number(static_cast(N)); - } + @return basically a string representation of a the @a m_type member - // step 2: write each element - for (const auto& el : *j.m_value.object) - { - write_msgpack(el.first); - write_msgpack(el.second); - } - break; - } + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + @since version 1.0.0, public since 2.1.0 + */ + std::string type_name() const + { + { + 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::discarded: + return "discarded"; default: - { - break; - } + return "number"; } } + } - private: - /* - @brief write a number to output input - - @param[in] n number of type @a NumberType - @tparam NumberType the type of the number - @note This function needs to respect the system's endianess, because - bytes in CBOR and MessagePack are stored in network order (big - endian) and therefore need reordering on little endian systems. - */ - template - void write_number(NumberType n) - { - // step 1: write number to array of length NumberType - std::array vec; - std::memcpy(vec.data(), &n, sizeof(NumberType)); + private: + ////////////////////// + // member variables // + ////////////////////// - // step 2: write array to output (with possible reordering) - if (is_little_endian) - { - // reverse byte order prior to conversion if necessary - std::reverse(vec.begin(), vec.end()); - } + /// the type of the current element + value_t m_type = value_t::null; - oa->write_characters(vec.data(), sizeof(NumberType)); - } + /// the value of the current element + json_value m_value = {}; - private: - /// whether we can assume little endianess - const bool is_little_endian = true; + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// - /// the output - output_adapter_t oa = nullptr; - }; + /// @name binary serialization/deserialization support + /// @{ public: /*! @@ -13163,7 +13208,7 @@ class basic_json static std::vector to_cbor(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(detail::output_adapter_factory::create(result)); bw.write_cbor(j); return result; } @@ -13245,7 +13290,7 @@ class basic_json static std::vector to_msgpack(const basic_json& j) { std::vector result; - binary_writer bw(output_adapter::create(result)); + binary_writer bw(detail::output_adapter_factory::create(result)); bw.write_msgpack(j); return result; } diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index f757541d9e..52746028ea 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -53,7 +53,7 @@ TEST_CASE("convenience functions") const char* escaped) { std::stringstream ss; - json::serializer s(json::output_adapter::create(ss), ' '); + json::serializer s(nlohmann::detail::output_adapter_factory::create(ss), ' '); s.dump_escaped(original); CHECK(ss.str() == escaped); };