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