mirror of
https://github.com/nlohmann/json.git
synced 2026-10-04 13:40:33 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42a22d2d5e | ||
|
|
45db371915 | ||
|
|
5d47284e6c | ||
|
|
3173c28dac | ||
|
|
e8239afff1 | ||
|
|
fd0261d909 | ||
|
|
fa2465b325 | ||
|
|
ee7c0ce71c | ||
|
|
2b29ca1812 |
@@ -57,6 +57,7 @@ cc_library(
|
|||||||
"include/nlohmann/detail/meta/type_traits.hpp",
|
"include/nlohmann/detail/meta/type_traits.hpp",
|
||||||
"include/nlohmann/detail/meta/void_t.hpp",
|
"include/nlohmann/detail/meta/void_t.hpp",
|
||||||
"include/nlohmann/detail/output/binary_writer.hpp",
|
"include/nlohmann/detail/output/binary_writer.hpp",
|
||||||
|
"include/nlohmann/detail/output/error_handler.hpp",
|
||||||
"include/nlohmann/detail/output/output_adapters.hpp",
|
"include/nlohmann/detail/output/output_adapters.hpp",
|
||||||
"include/nlohmann/detail/output/serializer.hpp",
|
"include/nlohmann/detail/output/serializer.hpp",
|
||||||
"include/nlohmann/detail/recursion_depth_limit.hpp",
|
"include/nlohmann/detail/recursion_depth_limit.hpp",
|
||||||
|
|||||||
+7
-3
@@ -596,8 +596,9 @@ foreach(SRC_FILE ${SRC_FILES})
|
|||||||
add_executable(single_${RELATIVE_SRC_FILE} EXCLUDE_FROM_ALL ${PROJECT_BINARY_DIR}/src_single/${RELATIVE_SRC_FILE}.cpp)
|
add_executable(single_${RELATIVE_SRC_FILE} EXCLUDE_FROM_ALL ${PROJECT_BINARY_DIR}/src_single/${RELATIVE_SRC_FILE}.cpp)
|
||||||
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
target_include_directories(single_${RELATIVE_SRC_FILE} PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
target_compile_features(single_${RELATIVE_SRC_FILE} PRIVATE cxx_std_11)
|
||||||
if(RELATIVE_SRC_FILE STREQUAL "json")
|
if(RELATIVE_SRC_FILE STREQUAL "json" OR RELATIVE_SRC_FILE STREQUAL "json_literals")
|
||||||
# see below: report json.hpp's diagnostics without --error, so they do not fail the build
|
# see below: report the diagnostics of json.hpp and json_literals.hpp without --error, so they
|
||||||
|
# do not fail the build
|
||||||
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_TOOL} -Xiwyu --max_line_length=300)
|
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE ${IWYU_TOOL} -Xiwyu --max_line_length=300)
|
||||||
else()
|
else()
|
||||||
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
set_property(TARGET single_${RELATIVE_SRC_FILE} PROPERTY CXX_INCLUDE_WHAT_YOU_USE "${iwyu_path_and_options}")
|
||||||
@@ -611,7 +612,10 @@ foreach(SRC_FILE ${SRC_FILES})
|
|||||||
# reporting its diagnostics (informational, via CXX_INCLUDE_WHAT_YOU_USE above) but exclude it
|
# reporting its diagnostics (informational, via CXX_INCLUDE_WHAT_YOU_USE above) but exclude it
|
||||||
# from the hard gate below so a fresh IWYU/compiler combination does not fail this target on a
|
# from the hard gate below so a fresh IWYU/compiler combination does not fail this target on a
|
||||||
# nondeterministic suggestion for a header that already re-exports everything on purpose.
|
# nondeterministic suggestion for a header that already re-exports everything on purpose.
|
||||||
if(NOT RELATIVE_SRC_FILE STREQUAL "json")
|
# json_literals.hpp and json.hpp include each other on purpose (json.hpp includes it at its end
|
||||||
|
# unless JSON_NO_AUTOMATIC_UDLS is defined), and IWYU, not following the cycle, suggests replacing
|
||||||
|
# json.hpp with json_fwd.hpp although the literals need the complete basic_json; exclude it, too.
|
||||||
|
if(NOT RELATIVE_SRC_FILE STREQUAL "json" AND NOT RELATIVE_SRC_FILE STREQUAL "json_literals")
|
||||||
list(APPEND single_binaries_tus src_single/${RELATIVE_SRC_FILE}.cpp)
|
list(APPEND single_binaries_tus src_single/${RELATIVE_SRC_FILE}.cpp)
|
||||||
endif()
|
endif()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ INSERT INTO searchIndex(name, type, path) VALUES ('JSON_NO_THREAD_LOCAL', 'Macro
|
|||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_PRECISE_STREAM_POSITION', 'Macro', 'api/macros/json_precise_stream_position/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_LIBRARY_VERSION_CHECK', 'Macro', 'api/macros/json_skip_library_version_check/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_SKIP_UNSUPPORTED_COMPILER_CHECK', 'Macro', 'api/macros/json_skip_unsupported_compiler_check/index.html');
|
||||||
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_BINARY_UTF8', 'Macro', 'api/macros/json_strict_binary_utf8/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_STRICT_NUL_HANDLING', 'Macro', 'api/macros/json_strict_nul_handling/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_THROW_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
||||||
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
INSERT INTO searchIndex(name, type, path) VALUES ('JSON_TRY_USER', 'Macro', 'api/macros/json_throw_user/index.html');
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example "Default behavior (macro not defined)"
|
??? example "Example: default behavior (macro not defined)"
|
||||||
|
|
||||||
Without the macro, the bytes are written unchanged:
|
Without the macro, the bytes are written unchanged:
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ The default value is `0` (disabled, the behavior of version 3.12.0 and earlier i
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Opt-in check (macro defined to 1)"
|
??? example "Example: opt-in check (macro defined to 1)"
|
||||||
|
|
||||||
With the macro, ill-formed UTF-8 is rejected:
|
With the macro, ill-formed UTF-8 is rejected:
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ By default, implicit conversions are enabled.
|
|||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
??? example
|
??? example "Example: implicit conversion"
|
||||||
|
|
||||||
This is an example for an implicit conversion:
|
This is an example for an implicit conversion:
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ By default, implicit conversions are enabled.
|
|||||||
auto s = j.get<std::string>();
|
auto s = j.get<std::string>();
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Conversion between `basic_json` specializations"
|
??? example "Example: conversion between `basic_json` specializations"
|
||||||
|
|
||||||
A `basic_json` specialization with a different string type is also no longer converted implicitly when
|
A `basic_json` specialization with a different string type is also no longer converted implicitly when
|
||||||
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
|
`JSON_USE_IMPLICIT_CONVERSIONS` is defined to `0`:
|
||||||
|
|||||||
@@ -110,15 +110,15 @@ class binary_reader
|
|||||||
|
|
||||||
@param[in] adapter input adapter to read from
|
@param[in] adapter input adapter to read from
|
||||||
@param[in] format the binary format to parse
|
@param[in] format the binary format to parse
|
||||||
@param[in] error_handler how to treat text strings and object keys that
|
@param[in] error_handler_ how to treat text strings and object keys that
|
||||||
are not well-formed UTF-8; none of the supported formats
|
are not well-formed UTF-8; none of the supported formats
|
||||||
requires a decoder to reject those, so the default is to
|
requires a decoder to reject those, so the default is to
|
||||||
@ref error_handler_t::keep them unchanged, as every binary
|
@ref error_handler_t::keep them unchanged, as every binary
|
||||||
reader did before this parameter existed
|
reader did before this parameter existed
|
||||||
*/
|
*/
|
||||||
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json,
|
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json,
|
||||||
const error_handler_t error_handler = error_handler_t::keep) noexcept
|
const error_handler_t error_handler_ = error_handler_t::keep) noexcept
|
||||||
: ia(std::move(adapter)), input_format(format), error_handler(error_handler)
|
: ia(std::move(adapter)), input_format(format), error_handler(error_handler_)
|
||||||
{
|
{
|
||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,6 +322,8 @@ class json_pointer
|
|||||||
typename BasicJsonType::size_type idx{};
|
typename BasicJsonType::size_type idx{};
|
||||||
switch (parse_array_index<BasicJsonType>(s, idx))
|
switch (parse_array_index<BasicJsonType>(s, idx))
|
||||||
{
|
{
|
||||||
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
@@ -685,6 +687,8 @@ class json_pointer
|
|||||||
typename BasicJsonType::size_type idx{};
|
typename BasicJsonType::size_type idx{};
|
||||||
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
||||||
{
|
{
|
||||||
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
|
|||||||
@@ -2260,18 +2260,18 @@ class binary_writer
|
|||||||
switch (error_handler)
|
switch (error_handler)
|
||||||
{
|
{
|
||||||
case error_handler_t::keep:
|
case error_handler_t::keep:
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
|
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
check_utf8(s, context);
|
check_utf8(s, context);
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
|
|
||||||
case error_handler_t::replace:
|
case error_handler_t::replace:
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
default:
|
default:
|
||||||
if (is_valid_utf8(s))
|
if (is_valid_utf8(s))
|
||||||
{
|
{
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
}
|
}
|
||||||
storage = sanitize_utf8(s, error_handler);
|
storage = sanitize_utf8(s, error_handler);
|
||||||
return storage;
|
return storage;
|
||||||
|
|||||||
@@ -706,6 +706,11 @@ class serializer
|
|||||||
@a ensure_ascii is a template parameter here so that the branch on it is
|
@a ensure_ascii is a template parameter here so that the branch on it is
|
||||||
resolved once, outside the loop; see @ref dump_escaped.
|
resolved once, outside the loop; see @ref dump_escaped.
|
||||||
*/
|
*/
|
||||||
|
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||||
|
#pragma warning(push)
|
||||||
|
// EnsureAscii is a template parameter; C++11 has no if constexpr
|
||||||
|
#pragma warning(disable : 4127) // conditional expression is constant
|
||||||
|
#endif
|
||||||
template<bool EnsureAscii>
|
template<bool EnsureAscii>
|
||||||
void dump_escaped_impl(const string_t& s)
|
void dump_escaped_impl(const string_t& s)
|
||||||
{
|
{
|
||||||
@@ -1055,6 +1060,9 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
|
|||||||
+36
-18
@@ -1048,6 +1048,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
never name it to call this constructor itself.
|
never name it to call this constructor itself.
|
||||||
*/
|
*/
|
||||||
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
||||||
|
noexcept(std::is_nothrow_copy_constructible<json_base_class_t>::value)
|
||||||
: json_base_class_t(src)
|
: json_base_class_t(src)
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
, start_position(src.start_position)
|
, start_position(src.start_position)
|
||||||
@@ -1442,7 +1443,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
be destroyed.
|
be destroyed.
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
void convert_iteratively(const BasicJsonType& val)
|
void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/)
|
||||||
{
|
{
|
||||||
using other_const_iterator = typename BasicJsonType::const_iterator;
|
using other_const_iterator = typename BasicJsonType::const_iterator;
|
||||||
|
|
||||||
@@ -1536,21 +1537,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
||||||
{
|
{
|
||||||
// every element comes back to the converting constructor
|
convert_by_serializers(val);
|
||||||
if (val.is_object())
|
|
||||||
{
|
|
||||||
using other_object_t = typename BasicJsonType::object_t;
|
|
||||||
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using other_array_t = typename BasicJsonType::array_t;
|
|
||||||
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_iteratively(val);
|
// the iterative conversion needs to construct this object type's keys
|
||||||
|
// from those of @a val; if it cannot, neither can the range constructor,
|
||||||
|
// and the serializers convert @a val some other way (see #3425)
|
||||||
|
convert_iteratively(val, std::is_constructible<typename object_t::key_type, const typename BasicJsonType::string_t&> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert the object or array @a val with the serializers; every
|
||||||
|
/// element comes back to the converting constructor
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void convert_by_serializers(const BasicJsonType& val)
|
||||||
|
{
|
||||||
|
if (val.is_object())
|
||||||
|
{
|
||||||
|
using other_object_t = typename BasicJsonType::object_t;
|
||||||
|
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using other_array_t = typename BasicJsonType::array_t;
|
||||||
|
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert @a val whose keys cannot be converted, see @ref convert_structured
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/)
|
||||||
|
{
|
||||||
|
convert_by_serializers(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1624,15 +1642,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||||
{
|
{
|
||||||
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
||||||
if (order == 0)
|
if (std::is_eq(order))
|
||||||
{
|
{
|
||||||
return compare_result::equal;
|
return compare_result::equal;
|
||||||
}
|
}
|
||||||
if (order < 0)
|
if (std::is_lt(order))
|
||||||
{
|
{
|
||||||
return compare_result::less;
|
return compare_result::less;
|
||||||
}
|
}
|
||||||
if (order > 0)
|
if (std::is_gt(order))
|
||||||
{
|
{
|
||||||
return compare_result::greater;
|
return compare_result::greater;
|
||||||
}
|
}
|
||||||
@@ -5058,7 +5076,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||||
template<typename ScalarType>
|
template<typename ScalarType>
|
||||||
requires std::is_scalar_v<ScalarType>
|
requires std::is_scalar_v<ScalarType>
|
||||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
|
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||||
{
|
{
|
||||||
return basic_json(lhs) <= rhs;
|
return basic_json(lhs) <= rhs;
|
||||||
}
|
}
|
||||||
@@ -5067,7 +5085,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||||
template<typename ScalarType>
|
template<typename ScalarType>
|
||||||
requires std::is_scalar_v<ScalarType>
|
requires std::is_scalar_v<ScalarType>
|
||||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
|
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||||
{
|
{
|
||||||
return basic_json(lhs) >= rhs;
|
return basic_json(lhs) >= rhs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <functional> // equal_to, less
|
#include <functional> // equal_to, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <iterator> // input_iterator_tag, iterator_traits
|
#include <iterator> // input_iterator_tag, iterator_traits
|
||||||
#include <memory> // allocator
|
#include <memory> // allocator // IWYU pragma: keep
|
||||||
#include <new> // for operator new (placement new)
|
#include <new> // for operator new (placement new)
|
||||||
#include <stdexcept> // for out_of_range
|
#include <stdexcept> // for out_of_range
|
||||||
#include <tuple> // forward_as_tuple
|
#include <tuple> // forward_as_tuple
|
||||||
@@ -78,7 +78,7 @@ private:
|
|||||||
/// @brief find the entry for @a key, for either constness of @a self
|
/// @brief find the entry for @a key, for either constness of @a self
|
||||||
/// @note the single place that performs the linear key search
|
/// @note the single place that performs the linear key search
|
||||||
template<typename Self, typename KeyType>
|
template<typename Self, typename KeyType>
|
||||||
static auto find_impl(Self& self, KeyType&& key) -> decltype(self.begin())
|
static auto find_impl(Self& self, const KeyType& key) -> decltype(self.begin())
|
||||||
{
|
{
|
||||||
for (auto it = self.begin(); it != self.end(); ++it)
|
for (auto it = self.begin(); it != self.end(); ++it)
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13696,15 +13696,15 @@ class binary_reader
|
|||||||
|
|
||||||
@param[in] adapter input adapter to read from
|
@param[in] adapter input adapter to read from
|
||||||
@param[in] format the binary format to parse
|
@param[in] format the binary format to parse
|
||||||
@param[in] error_handler how to treat text strings and object keys that
|
@param[in] error_handler_ how to treat text strings and object keys that
|
||||||
are not well-formed UTF-8; none of the supported formats
|
are not well-formed UTF-8; none of the supported formats
|
||||||
requires a decoder to reject those, so the default is to
|
requires a decoder to reject those, so the default is to
|
||||||
@ref error_handler_t::keep them unchanged, as every binary
|
@ref error_handler_t::keep them unchanged, as every binary
|
||||||
reader did before this parameter existed
|
reader did before this parameter existed
|
||||||
*/
|
*/
|
||||||
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json,
|
explicit binary_reader(InputAdapterType&& adapter, const input_format_t format = input_format_t::json,
|
||||||
const error_handler_t error_handler = error_handler_t::keep) noexcept
|
const error_handler_t error_handler_ = error_handler_t::keep) noexcept
|
||||||
: ia(std::move(adapter)), input_format(format), error_handler(error_handler)
|
: ia(std::move(adapter)), input_format(format), error_handler(error_handler_)
|
||||||
{
|
{
|
||||||
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
(void)detail::is_sax_static_asserts<SAX, BasicJsonType> {};
|
||||||
}
|
}
|
||||||
@@ -19998,6 +19998,8 @@ class json_pointer
|
|||||||
typename BasicJsonType::size_type idx{};
|
typename BasicJsonType::size_type idx{};
|
||||||
switch (parse_array_index<BasicJsonType>(s, idx))
|
switch (parse_array_index<BasicJsonType>(s, idx))
|
||||||
{
|
{
|
||||||
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", s, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
@@ -20361,6 +20363,8 @@ class json_pointer
|
|||||||
typename BasicJsonType::size_type idx{};
|
typename BasicJsonType::size_type idx{};
|
||||||
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
switch (parse_array_index<BasicJsonType>(reference_token, idx))
|
||||||
{
|
{
|
||||||
|
// the branches differ in their messages, not after JSON_THROW's expansion
|
||||||
|
// NOLINTNEXTLINE(bugprone-branch-clone)
|
||||||
case array_index_status::leading_zero:
|
case array_index_status::leading_zero:
|
||||||
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
JSON_THROW(detail::parse_error::create(106, 0, detail::concat("array index '", reference_token, "' must not begin with '0'"), nullptr));
|
||||||
case array_index_status::not_a_number:
|
case array_index_status::not_a_number:
|
||||||
@@ -23485,18 +23489,18 @@ class binary_writer
|
|||||||
switch (error_handler)
|
switch (error_handler)
|
||||||
{
|
{
|
||||||
case error_handler_t::keep:
|
case error_handler_t::keep:
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
|
|
||||||
case error_handler_t::strict:
|
case error_handler_t::strict:
|
||||||
check_utf8(s, context);
|
check_utf8(s, context);
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
|
|
||||||
case error_handler_t::replace:
|
case error_handler_t::replace:
|
||||||
case error_handler_t::ignore:
|
case error_handler_t::ignore:
|
||||||
default:
|
default:
|
||||||
if (is_valid_utf8(s))
|
if (is_valid_utf8(s))
|
||||||
{
|
{
|
||||||
return s;
|
return s; // NOLINT(bugprone-return-const-ref-from-parameter): callers pass lvalues that outlive the call
|
||||||
}
|
}
|
||||||
storage = sanitize_utf8(s, error_handler);
|
storage = sanitize_utf8(s, error_handler);
|
||||||
return storage;
|
return storage;
|
||||||
@@ -25681,6 +25685,11 @@ class serializer
|
|||||||
@a ensure_ascii is a template parameter here so that the branch on it is
|
@a ensure_ascii is a template parameter here so that the branch on it is
|
||||||
resolved once, outside the loop; see @ref dump_escaped.
|
resolved once, outside the loop; see @ref dump_escaped.
|
||||||
*/
|
*/
|
||||||
|
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||||
|
#pragma warning(push)
|
||||||
|
// EnsureAscii is a template parameter; C++11 has no if constexpr
|
||||||
|
#pragma warning(disable : 4127) // conditional expression is constant
|
||||||
|
#endif
|
||||||
template<bool EnsureAscii>
|
template<bool EnsureAscii>
|
||||||
void dump_escaped_impl(const string_t& s)
|
void dump_escaped_impl(const string_t& s)
|
||||||
{
|
{
|
||||||
@@ -26030,6 +26039,9 @@ class serializer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef JSON_HEDLEY_MSVC_VERSION
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
@@ -26612,7 +26624,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <functional> // equal_to, less
|
#include <functional> // equal_to, less
|
||||||
#include <initializer_list> // initializer_list
|
#include <initializer_list> // initializer_list
|
||||||
#include <iterator> // input_iterator_tag, iterator_traits
|
#include <iterator> // input_iterator_tag, iterator_traits
|
||||||
#include <memory> // allocator
|
#include <memory> // allocator // IWYU pragma: keep
|
||||||
#include <new> // for operator new (placement new)
|
#include <new> // for operator new (placement new)
|
||||||
#include <stdexcept> // for out_of_range
|
#include <stdexcept> // for out_of_range
|
||||||
#include <tuple> // forward_as_tuple
|
#include <tuple> // forward_as_tuple
|
||||||
@@ -26681,7 +26693,7 @@ private:
|
|||||||
/// @brief find the entry for @a key, for either constness of @a self
|
/// @brief find the entry for @a key, for either constness of @a self
|
||||||
/// @note the single place that performs the linear key search
|
/// @note the single place that performs the linear key search
|
||||||
template<typename Self, typename KeyType>
|
template<typename Self, typename KeyType>
|
||||||
static auto find_impl(Self& self, KeyType&& key) -> decltype(self.begin())
|
static auto find_impl(Self& self, const KeyType& key) -> decltype(self.begin())
|
||||||
{
|
{
|
||||||
for (auto it = self.begin(); it != self.end(); ++it)
|
for (auto it = self.begin(); it != self.end(); ++it)
|
||||||
{
|
{
|
||||||
@@ -27985,6 +27997,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
never name it to call this constructor itself.
|
never name it to call this constructor itself.
|
||||||
*/
|
*/
|
||||||
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
basic_json(copy_construct_tag /*unused*/, const basic_json& src)
|
||||||
|
noexcept(std::is_nothrow_copy_constructible<json_base_class_t>::value)
|
||||||
: json_base_class_t(src)
|
: json_base_class_t(src)
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
, start_position(src.start_position)
|
, start_position(src.start_position)
|
||||||
@@ -28379,7 +28392,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
be destroyed.
|
be destroyed.
|
||||||
*/
|
*/
|
||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
void convert_iteratively(const BasicJsonType& val)
|
void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/)
|
||||||
{
|
{
|
||||||
using other_const_iterator = typename BasicJsonType::const_iterator;
|
using other_const_iterator = typename BasicJsonType::const_iterator;
|
||||||
|
|
||||||
@@ -28473,21 +28486,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
|
|
||||||
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
if (JSON_HEDLEY_LIKELY(guard.okay()))
|
||||||
{
|
{
|
||||||
// every element comes back to the converting constructor
|
convert_by_serializers(val);
|
||||||
if (val.is_object())
|
|
||||||
{
|
|
||||||
using other_object_t = typename BasicJsonType::object_t;
|
|
||||||
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
using other_array_t = typename BasicJsonType::array_t;
|
|
||||||
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_iteratively(val);
|
// the iterative conversion needs to construct this object type's keys
|
||||||
|
// from those of @a val; if it cannot, neither can the range constructor,
|
||||||
|
// and the serializers convert @a val some other way (see #3425)
|
||||||
|
convert_iteratively(val, std::is_constructible<typename object_t::key_type, const typename BasicJsonType::string_t&> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert the object or array @a val with the serializers; every
|
||||||
|
/// element comes back to the converting constructor
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void convert_by_serializers(const BasicJsonType& val)
|
||||||
|
{
|
||||||
|
if (val.is_object())
|
||||||
|
{
|
||||||
|
using other_object_t = typename BasicJsonType::object_t;
|
||||||
|
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using other_array_t = typename BasicJsonType::array_t;
|
||||||
|
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief convert @a val whose keys cannot be converted, see @ref convert_structured
|
||||||
|
template<typename BasicJsonType>
|
||||||
|
void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/)
|
||||||
|
{
|
||||||
|
convert_by_serializers(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -28561,15 +28591,15 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
static compare_result compare_leaves(const_reference lhs, const_reference rhs, std::true_type /*ordered*/) noexcept
|
||||||
{
|
{
|
||||||
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
const std::partial_ordering order = lhs <=> rhs; // *NOPAD*
|
||||||
if (order == 0)
|
if (std::is_eq(order))
|
||||||
{
|
{
|
||||||
return compare_result::equal;
|
return compare_result::equal;
|
||||||
}
|
}
|
||||||
if (order < 0)
|
if (std::is_lt(order))
|
||||||
{
|
{
|
||||||
return compare_result::less;
|
return compare_result::less;
|
||||||
}
|
}
|
||||||
if (order > 0)
|
if (std::is_gt(order))
|
||||||
{
|
{
|
||||||
return compare_result::greater;
|
return compare_result::greater;
|
||||||
}
|
}
|
||||||
@@ -31995,7 +32025,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_le/
|
||||||
template<typename ScalarType>
|
template<typename ScalarType>
|
||||||
requires std::is_scalar_v<ScalarType>
|
requires std::is_scalar_v<ScalarType>
|
||||||
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept
|
friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||||
{
|
{
|
||||||
return basic_json(lhs) <= rhs;
|
return basic_json(lhs) <= rhs;
|
||||||
}
|
}
|
||||||
@@ -32004,7 +32034,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
|||||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
/// @sa https://json.nlohmann.me/api/basic_json/operator_ge/
|
||||||
template<typename ScalarType>
|
template<typename ScalarType>
|
||||||
requires std::is_scalar_v<ScalarType>
|
requires std::is_scalar_v<ScalarType>
|
||||||
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept
|
friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept(std::is_nothrow_constructible<basic_json, ScalarType>::value)
|
||||||
{
|
{
|
||||||
return basic_json(lhs) >= rhs;
|
return basic_json(lhs) >= rhs;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
#include <cstdint> // uint8_t
|
#include <cstdint> // uint8_t
|
||||||
#include <cstddef> // size_t
|
#include <cstddef> // size_t
|
||||||
#include <fstream> // ifstream, istreambuf_iterator, ios
|
#include <fstream> // ifstream, ios
|
||||||
|
#include <iterator> // istream_iterator
|
||||||
#include <vector> // vector
|
#include <vector> // vector
|
||||||
|
|
||||||
namespace utils
|
namespace utils
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ struct allocator_no_forward : std::allocator<T>
|
|||||||
{
|
{
|
||||||
allocator_no_forward() = default;
|
allocator_no_forward() = default;
|
||||||
template <class U>
|
template <class U>
|
||||||
allocator_no_forward(allocator_no_forward<U> /*unused*/) {}
|
allocator_no_forward(const allocator_no_forward<U>& /*unused*/) {}
|
||||||
|
|
||||||
template <class U>
|
template <class U>
|
||||||
struct rebind
|
struct rebind
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
#include "test_utils.hpp"
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
@@ -25,18 +26,27 @@ struct ill_formed_case
|
|||||||
|
|
||||||
// RFC 3629 ill-formed sequences used throughout this file, plus one
|
// RFC 3629 ill-formed sequences used throughout this file, plus one
|
||||||
// well-formed sequence for contrast
|
// well-formed sequence for contrast
|
||||||
const std::vector<ill_formed_case> ill_formed_cases =
|
std::vector<ill_formed_case> ill_formed_cases()
|
||||||
{
|
{
|
||||||
{"overlong", "\xC0\xAE"},
|
return
|
||||||
{"lone_0xFF", "\xFF"},
|
{
|
||||||
{"truncated", "\xE2\x82"},
|
{"overlong", "\xC0\xAE"},
|
||||||
{"surrogate", "\xED\xA0\x80"},
|
{"lone_0xFF", "\xFF"},
|
||||||
};
|
{"truncated", "\xE2\x82"},
|
||||||
|
{"surrogate", "\xED\xA0\x80"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const std::string valid_sequence = "\xC3\xA9"; // U+00E9, "é"
|
std::string valid_sequence()
|
||||||
|
{
|
||||||
|
return "\xC3\xA9"; // U+00E9, "é"
|
||||||
|
}
|
||||||
|
|
||||||
using eh = json::error_handler_t;
|
using eh = json::error_handler_t;
|
||||||
const std::vector<eh> all_handlers = {eh::strict, eh::replace, eh::ignore, eh::keep};
|
std::vector<eh> all_handlers()
|
||||||
|
{
|
||||||
|
return {eh::strict, eh::replace, eh::ignore, eh::keep};
|
||||||
|
}
|
||||||
|
|
||||||
// what dump()+parse() produces for a sanitizing error_handler; this is the
|
// what dump()+parse() produces for a sanitizing error_handler; this is the
|
||||||
// ground truth every binary writer/reader is checked against
|
// ground truth every binary writer/reader is checked against
|
||||||
@@ -51,9 +61,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
{
|
{
|
||||||
SECTION("writers: string value")
|
SECTION("writers: string value")
|
||||||
{
|
{
|
||||||
for (const auto& c : ill_formed_cases)
|
for (const auto& c : ill_formed_cases())
|
||||||
{
|
{
|
||||||
CAPTURE(c.name);
|
CAPTURE(c.name)
|
||||||
const json jval = c.bytes;
|
const json jval = c.bytes;
|
||||||
|
|
||||||
CHECK_THROWS_AS(json::to_cbor(jval, eh::strict), json::type_error&);
|
CHECK_THROWS_AS(json::to_cbor(jval, eh::strict), json::type_error&);
|
||||||
@@ -71,7 +81,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
eh::replace, eh::ignore
|
eh::replace, eh::ignore
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(static_cast<int>(h));
|
CAPTURE(static_cast<int>(h))
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
const std::string expected = dump_and_parse(c.bytes, h);
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == expected);
|
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == expected);
|
||||||
@@ -103,9 +113,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("writers: object key")
|
SECTION("writers: object key")
|
||||||
{
|
{
|
||||||
for (const auto& c : ill_formed_cases)
|
for (const auto& c : ill_formed_cases())
|
||||||
{
|
{
|
||||||
CAPTURE(c.name);
|
CAPTURE(c.name)
|
||||||
json jobj;
|
json jobj;
|
||||||
jobj[c.bytes] = 1;
|
jobj[c.bytes] = 1;
|
||||||
|
|
||||||
@@ -120,7 +130,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
eh::replace, eh::ignore
|
eh::replace, eh::ignore
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(static_cast<int>(h));
|
CAPTURE(static_cast<int>(h))
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
const std::string expected = dump_and_parse(c.bytes, h);
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jobj, h)).begin().key() == expected);
|
CHECK(json::from_cbor(json::to_cbor(jobj, h)).begin().key() == expected);
|
||||||
@@ -141,9 +151,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("readers: string value")
|
SECTION("readers: string value")
|
||||||
{
|
{
|
||||||
for (const auto& c : ill_formed_cases)
|
for (const auto& c : ill_formed_cases())
|
||||||
{
|
{
|
||||||
CAPTURE(c.name);
|
CAPTURE(c.name)
|
||||||
|
|
||||||
// bytes produced the lenient (keep) way, as any binary reader
|
// bytes produced the lenient (keep) way, as any binary reader
|
||||||
// accepted them before this parameter existed
|
// accepted them before this parameter existed
|
||||||
@@ -166,15 +176,15 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == c.bytes);
|
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == c.bytes);
|
||||||
|
|
||||||
// strict: parse_error.113, discarded (not thrown) when allow_exceptions is false
|
// strict: parse_error.113, discarded (not thrown) when allow_exceptions is false
|
||||||
CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict)), json::parse_error&);
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, false, json::cbor_tag_handler_t::error, eh::strict).is_discarded());
|
CHECK(json::from_cbor(cbor_bytes, true, false, json::cbor_tag_handler_t::error, eh::strict).is_discarded());
|
||||||
CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_msgpack(msgpack_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK(json::from_msgpack(msgpack_bytes, true, false, eh::strict).is_discarded());
|
CHECK(json::from_msgpack(msgpack_bytes, true, false, eh::strict).is_discarded());
|
||||||
CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_ubjson(ubjson_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK(json::from_ubjson(ubjson_bytes, true, false, eh::strict).is_discarded());
|
CHECK(json::from_ubjson(ubjson_bytes, true, false, eh::strict).is_discarded());
|
||||||
CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_bjdata(bjdata_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK(json::from_bjdata(bjdata_bytes, true, false, eh::strict).is_discarded());
|
CHECK(json::from_bjdata(bjdata_bytes, true, false, eh::strict).is_discarded());
|
||||||
CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_bson(bson_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK(json::from_bson(bson_bytes, true, false, eh::strict).is_discarded());
|
CHECK(json::from_bson(bson_bytes, true, false, eh::strict).is_discarded());
|
||||||
|
|
||||||
// replace / ignore: match what dump() would have sanitized the same bytes to
|
// replace / ignore: match what dump() would have sanitized the same bytes to
|
||||||
@@ -183,7 +193,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
eh::replace, eh::ignore
|
eh::replace, eh::ignore
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(static_cast<int>(h));
|
CAPTURE(static_cast<int>(h))
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
const std::string expected = dump_and_parse(c.bytes, h);
|
||||||
|
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == expected);
|
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == expected);
|
||||||
@@ -197,9 +207,9 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("readers: object key")
|
SECTION("readers: object key")
|
||||||
{
|
{
|
||||||
for (const auto& c : ill_formed_cases)
|
for (const auto& c : ill_formed_cases())
|
||||||
{
|
{
|
||||||
CAPTURE(c.name);
|
CAPTURE(c.name)
|
||||||
|
|
||||||
json jobj;
|
json jobj;
|
||||||
jobj[c.bytes] = 1;
|
jobj[c.bytes] = 1;
|
||||||
@@ -215,18 +225,18 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
CHECK(json::from_bjdata(bjdata_bytes).begin().key() == c.bytes);
|
CHECK(json::from_bjdata(bjdata_bytes).begin().key() == c.bytes);
|
||||||
CHECK(json::from_bson(bson_bytes).begin().key() == c.bytes);
|
CHECK(json::from_bson(bson_bytes).begin().key() == c.bytes);
|
||||||
|
|
||||||
CHECK_THROWS_AS(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, eh::strict)), json::parse_error&);
|
||||||
CHECK_THROWS_AS(json::from_msgpack(msgpack_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_msgpack(msgpack_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK_THROWS_AS(json::from_ubjson(ubjson_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_ubjson(ubjson_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK_THROWS_AS(json::from_bjdata(bjdata_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_bjdata(bjdata_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
CHECK_THROWS_AS(json::from_bson(bson_bytes, true, true, eh::strict), json::parse_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(json::from_bson(bson_bytes, true, true, eh::strict)), json::parse_error&);
|
||||||
|
|
||||||
for (const auto h :
|
for (const auto h :
|
||||||
{
|
{
|
||||||
eh::replace, eh::ignore
|
eh::replace, eh::ignore
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(static_cast<int>(h));
|
CAPTURE(static_cast<int>(h))
|
||||||
const std::string expected = dump_and_parse(c.bytes, h);
|
const std::string expected = dump_and_parse(c.bytes, h);
|
||||||
|
|
||||||
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).begin().key() == expected);
|
CHECK(json::from_cbor(cbor_bytes, true, true, json::cbor_tag_handler_t::error, h).begin().key() == expected);
|
||||||
@@ -240,30 +250,30 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("well-formed UTF-8 is unaffected by error_handler")
|
SECTION("well-formed UTF-8 is unaffected by error_handler")
|
||||||
{
|
{
|
||||||
const json jval = valid_sequence;
|
const json jval = valid_sequence();
|
||||||
json jobj;
|
json jobj;
|
||||||
jobj[valid_sequence] = valid_sequence;
|
jobj[valid_sequence()] = valid_sequence();
|
||||||
|
|
||||||
for (const auto h : all_handlers)
|
for (const auto h : all_handlers())
|
||||||
{
|
{
|
||||||
CAPTURE(static_cast<int>(h));
|
CAPTURE(static_cast<int>(h))
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == valid_sequence);
|
CHECK(json::from_cbor(json::to_cbor(jval, h)).get<std::string>() == valid_sequence());
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get<std::string>() == valid_sequence);
|
CHECK(json::from_msgpack(json::to_msgpack(jval, h)).get<std::string>() == valid_sequence());
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get<std::string>() == valid_sequence);
|
CHECK(json::from_ubjson(json::to_ubjson(jval, false, false, h)).get<std::string>() == valid_sequence());
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get<std::string>() == valid_sequence);
|
CHECK(json::from_bjdata(json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, h)).get<std::string>() == valid_sequence());
|
||||||
CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == valid_sequence);
|
CHECK(json::from_bson(json::to_bson(jobj, h)).begin().key() == valid_sequence());
|
||||||
|
|
||||||
CHECK(json::from_cbor(json::to_cbor(jval, eh::keep), true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == valid_sequence);
|
CHECK(json::from_cbor(json::to_cbor(jval, eh::keep), true, true, json::cbor_tag_handler_t::error, h).get<std::string>() == valid_sequence());
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval), true, true, h).get<std::string>() == valid_sequence);
|
CHECK(json::from_msgpack(json::to_msgpack(jval), true, true, h).get<std::string>() == valid_sequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("dump() with error_handler_t::keep writes raw bytes as is")
|
SECTION("dump() with error_handler_t::keep writes raw bytes as is")
|
||||||
{
|
{
|
||||||
for (const auto& c : ill_formed_cases)
|
for (const auto& c : ill_formed_cases())
|
||||||
{
|
{
|
||||||
CAPTURE(c.name);
|
CAPTURE(c.name)
|
||||||
|
|
||||||
const json jval = c.bytes;
|
const json jval = c.bytes;
|
||||||
const std::string dumped = jval.dump(-1, ' ', false, eh::keep);
|
const std::string dumped = jval.dump(-1, ' ', false, eh::keep);
|
||||||
@@ -276,10 +286,10 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
// well-formed characters around an ill-formed sequence are still
|
// well-formed characters around an ill-formed sequence are still
|
||||||
// escaped as usual under ensure_ascii
|
// escaped as usual under ensure_ascii
|
||||||
const json mixed = valid_sequence + ill_formed_cases[1].bytes; // "é" + lone 0xFF
|
const json mixed = valid_sequence() + ill_formed_cases()[1].bytes; // "é" + lone 0xFF
|
||||||
const std::string dumped_mixed = mixed.dump(-1, ' ', true, eh::keep);
|
const std::string dumped_mixed = mixed.dump(-1, ' ', true, eh::keep);
|
||||||
CHECK(dumped_mixed.find("\\u00e9") != std::string::npos);
|
CHECK(dumped_mixed.find("\\u00e9") != std::string::npos);
|
||||||
CHECK(dumped_mixed.find(ill_formed_cases[1].bytes) != std::string::npos);
|
CHECK(dumped_mixed.find(ill_formed_cases()[1].bytes) != std::string::npos);
|
||||||
|
|
||||||
// the byte that ends an ill-formed sequence is read again, so a quote,
|
// the byte that ends an ill-formed sequence is read again, so a quote,
|
||||||
// a backslash, or a control character after it is still escaped, and
|
// a backslash, or a control character after it is still escaped, and
|
||||||
@@ -289,7 +299,7 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
false, true
|
false, true
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
CAPTURE(ensure_ascii);
|
CAPTURE(ensure_ascii)
|
||||||
CHECK(json("\xC3\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\"\"");
|
CHECK(json("\xC3\"").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\"\"");
|
||||||
CHECK(json("\xC3\\").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\\\"");
|
CHECK(json("\xC3\\").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\\\\"");
|
||||||
CHECK(json("\xC3\n").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\n\"");
|
CHECK(json("\xC3\n").dump(-1, ' ', ensure_ascii, eh::keep) == "\"\xC3\\n\"");
|
||||||
@@ -303,12 +313,12 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("to_msgpack defaults to keep; to_bon8 is not affected by error_handler")
|
SECTION("to_msgpack defaults to keep; to_bon8 is not affected by error_handler")
|
||||||
{
|
{
|
||||||
const json jval = ill_formed_cases[1].bytes; // lone 0xFF
|
const json jval = ill_formed_cases()[1].bytes; // lone 0xFF
|
||||||
|
|
||||||
// to_msgpack's error_handler defaults to keep, as MessagePack's spec
|
// to_msgpack's error_handler defaults to keep, as MessagePack's spec
|
||||||
// allows any bytes in a str, so the bytes are passed through
|
// allows any bytes in a str, so the bytes are passed through
|
||||||
CHECK(json::to_msgpack(jval) == json::to_msgpack(jval, eh::keep));
|
CHECK(json::to_msgpack(jval) == json::to_msgpack(jval, eh::keep));
|
||||||
CHECK(json::from_msgpack(json::to_msgpack(jval)).get<std::string>() == ill_formed_cases[1].bytes);
|
CHECK(json::from_msgpack(json::to_msgpack(jval)).get<std::string>() == ill_formed_cases()[1].bytes);
|
||||||
|
|
||||||
// the diagnostics context of an ill-formed key is the object
|
// the diagnostics context of an ill-formed key is the object
|
||||||
json jobj;
|
json jobj;
|
||||||
@@ -322,14 +332,14 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
|
|
||||||
SECTION("allow_exceptions=false with error_handler_t::strict discards the value")
|
SECTION("allow_exceptions=false with error_handler_t::strict discards the value")
|
||||||
{
|
{
|
||||||
const auto bytes = json::to_cbor(json(ill_formed_cases[0].bytes), eh::keep);
|
const auto bytes = json::to_cbor(json(ill_formed_cases()[0].bytes), eh::keep);
|
||||||
const json result = json::from_cbor(bytes, true, false, json::cbor_tag_handler_t::error, eh::strict);
|
const json result = json::from_cbor(bytes, true, false, json::cbor_tag_handler_t::error, eh::strict);
|
||||||
CHECK(result.is_discarded());
|
CHECK(result.is_discarded());
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("default parameters are unchanged")
|
SECTION("default parameters are unchanged")
|
||||||
{
|
{
|
||||||
const json jval = ill_formed_cases[0].bytes;
|
const json jval = ill_formed_cases()[0].bytes;
|
||||||
|
|
||||||
// to_*: the default error_handler is keep, so ill-formed bytes are
|
// to_*: the default error_handler is keep, so ill-formed bytes are
|
||||||
// written unchanged, exactly as in release 3.12.0 (it is strict only
|
// written unchanged, exactly as in release 3.12.0 (it is strict only
|
||||||
@@ -347,16 +357,16 @@ TEST_CASE("UTF-8 error_handler for the binary readers and writers")
|
|||||||
// from_*: the default error_handler is keep, so ill-formed bytes are
|
// from_*: the default error_handler is keep, so ill-formed bytes are
|
||||||
// still accepted unchanged, exactly as in release 3.12.0
|
// still accepted unchanged, exactly as in release 3.12.0
|
||||||
const auto cbor_bytes = json::to_cbor(jval, eh::keep);
|
const auto cbor_bytes = json::to_cbor(jval, eh::keep);
|
||||||
CHECK(json::from_cbor(cbor_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
CHECK(json::from_cbor(cbor_bytes).get<std::string>() == ill_formed_cases()[0].bytes);
|
||||||
const auto ubjson_bytes = json::to_ubjson(jval, false, false, eh::keep);
|
const auto ubjson_bytes = json::to_ubjson(jval, false, false, eh::keep);
|
||||||
CHECK(json::from_ubjson(ubjson_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
CHECK(json::from_ubjson(ubjson_bytes).get<std::string>() == ill_formed_cases()[0].bytes);
|
||||||
const auto bjdata_bytes = json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep);
|
const auto bjdata_bytes = json::to_bjdata(jval, false, false, json::bjdata_version_t::draft2, eh::keep);
|
||||||
CHECK(json::from_bjdata(bjdata_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
CHECK(json::from_bjdata(bjdata_bytes).get<std::string>() == ill_formed_cases()[0].bytes);
|
||||||
const auto msgpack_bytes = json::to_msgpack(jval);
|
const auto msgpack_bytes = json::to_msgpack(jval);
|
||||||
CHECK(json::from_msgpack(msgpack_bytes).get<std::string>() == ill_formed_cases[0].bytes);
|
CHECK(json::from_msgpack(msgpack_bytes).get<std::string>() == ill_formed_cases()[0].bytes);
|
||||||
json bson_obj;
|
json bson_obj;
|
||||||
bson_obj["k"] = jval;
|
bson_obj["k"] = jval;
|
||||||
const auto bson_bytes = json::to_bson(bson_obj, eh::keep);
|
const auto bson_bytes = json::to_bson(bson_obj, eh::keep);
|
||||||
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == ill_formed_cases[0].bytes);
|
CHECK(json::from_bson(bson_bytes)["k"].get<std::string>() == ill_formed_cases()[0].bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,21 +83,45 @@ TEST_CASE("JSON_STRICT_BINARY_UTF8 (see #5529, #5651)")
|
|||||||
// any bytes reach the output adapter (the BSON document length
|
// any bytes reach the output adapter (the BSON document length
|
||||||
// prefix must be known up front, so nothing is written incrementally)
|
// prefix must be known up front, so nothing is written incrementally)
|
||||||
std::vector<std::uint8_t> out{0x42}; // a sentinel byte the writer must not touch
|
std::vector<std::uint8_t> out{0x42}; // a sentinel byte the writer must not touch
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}, nlohmann::detail::output_adapter<std::uint8_t>(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}, nlohmann::detail::output_adapter<std::uint8_t>(out)), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}, nlohmann::detail::output_adapter<std::uint8_t>(out)), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#endif
|
||||||
CHECK(out == std::vector<std::uint8_t> {0x42});
|
CHECK(out == std::vector<std::uint8_t> {0x42});
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xFF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#endif
|
||||||
// a truncated multi-byte sequence
|
// a truncated multi-byte sequence
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC3"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC3"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC3", json::type_error&);
|
||||||
|
#endif
|
||||||
// an encoded surrogate half (U+D800)
|
// an encoded surrogate half (U+D800)
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xED\xA0\x80"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xED", json::type_error&);
|
||||||
|
#endif
|
||||||
// an overlong encoding of '.'
|
// an overlong encoding of '.'
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] (/s) invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"s", "\xC0\xAF"}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xC0", json::type_error&);
|
||||||
|
#endif
|
||||||
|
|
||||||
// an object key with ill-formed UTF-8 is rejected as well; unlike
|
// an object key with ill-formed UTF-8 is rejected as well; unlike
|
||||||
// the reader (which never validates element names), the writer
|
// the reader (which never validates element names), the writer
|
||||||
// checks both string values and object keys
|
// checks both string values and object keys
|
||||||
CHECK_THROWS_WITH_AS(json::to_bson(json{{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"\xFF", 1}}), "[json.exception.type_error.316] (/\xFF) invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(json::to_bson(json {{"\xFF", 1}}), "[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF", json::type_error&);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("an explicit error_handler overrides the default")
|
SECTION("an explicit error_handler overrides the default")
|
||||||
|
|||||||
@@ -3921,7 +3921,7 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK_NOTHROW(j = json::from_bjdata(v));
|
CHECK_NOTHROW(j = json::from_bjdata(v));
|
||||||
REQUIRE(j.is_string());
|
REQUIRE(j.is_string());
|
||||||
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||||
CHECK(json::from_bjdata(json::to_bjdata(j)) == j);
|
CHECK(json::from_bjdata(json::to_bjdata(j)) == j);
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
// the same bytes as an object key round-trip as well
|
||||||
|
|||||||
@@ -786,6 +786,7 @@ TEST_CASE("Parse BON8 directly from a file using iterator and sentinel")
|
|||||||
CHECK((parsed.is_object() || parsed.is_array()));
|
CHECK((parsed.is_object() || parsed.is_array()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION) // corpus values that do not survive the round trip are skipped by catching the exception
|
||||||
TEST_CASE("BON8 round-trip invariants")
|
TEST_CASE("BON8 round-trip invariants")
|
||||||
{
|
{
|
||||||
// This checks what the parse_bon8_fuzzer driver checks (see
|
// This checks what the parse_bon8_fuzzer driver checks (see
|
||||||
@@ -818,6 +819,7 @@ TEST_CASE("BON8 round-trip invariants")
|
|||||||
CHECK(json::to_bon8(j2) == vec);
|
CHECK(json::to_bon8(j2) == vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("BON8 roundtrips" * doctest::skip())
|
TEST_CASE("BON8 roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ class huge_string_t : public std::string
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using std::string::string;
|
using std::string::string;
|
||||||
|
// inheriting std::string's constructors does not inherit its default constructor
|
||||||
|
huge_string_t() = default;
|
||||||
huge_string_t(const std::string& s) : std::string(s) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
huge_string_t(const std::string& s) : std::string(s) {} // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||||
|
|
||||||
// returns a copy of @a s whose size() pretends to be huge
|
// returns a copy of @a s whose size() pretends to be huge
|
||||||
@@ -174,7 +176,7 @@ TEST_CASE("BSON")
|
|||||||
REQUIRE(j.contains("s"));
|
REQUIRE(j.contains("s"));
|
||||||
CHECK(j["s"].get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
CHECK(j["s"].get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
// dump() still requires valid UTF-8 and throws for such a value
|
// dump() still requires valid UTF-8 and throws for such a value
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||||
// to_bson() writes the bytes back unchanged, as before 3.13.0,
|
// to_bson() writes the bytes back unchanged, as before 3.13.0,
|
||||||
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
||||||
CHECK(json::from_bson(json::to_bson(j)) == j);
|
CHECK(json::from_bson(json::to_bson(j)) == j);
|
||||||
|
|||||||
@@ -1820,7 +1820,7 @@ TEST_CASE("CBOR")
|
|||||||
// dump() still requires valid UTF-8 and throws for such a value,
|
// dump() still requires valid UTF-8 and throws for such a value,
|
||||||
// unless an error handler that replaces or ignores the bytes is
|
// unless an error handler that replaces or ignores the bytes is
|
||||||
// passed
|
// passed
|
||||||
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(j_value.dump()), json::type_error&);
|
||||||
// to_cbor() writes the bytes back unchanged, as before 3.13.0,
|
// to_cbor() writes the bytes back unchanged, as before 3.13.0,
|
||||||
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
// unless JSON_STRICT_BINARY_UTF8 is enabled (see unit-binary_utf8_strict.cpp)
|
||||||
CHECK(json::from_cbor(json::to_cbor(j_value)) == j_value);
|
CHECK(json::from_cbor(json::to_cbor(j_value)) == j_value);
|
||||||
@@ -1878,13 +1878,13 @@ TEST_CASE("CBOR")
|
|||||||
// a truncated code point is kept as is
|
// a truncated code point is kept as is
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0xff})));
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x61, 0xc3, 0xff})));
|
||||||
CHECK(_ == "\xc3");
|
CHECK(_ == "\xc3");
|
||||||
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(_.dump()), json::type_error&);
|
||||||
CHECK(json::from_cbor(json::to_cbor(_)) == _);
|
CHECK(json::from_cbor(json::to_cbor(_)) == _);
|
||||||
|
|
||||||
// an ill-formed later chunk is kept after valid ones
|
// an ill-formed later chunk is kept after valid ones
|
||||||
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})));
|
CHECK_NOTHROW(_ = json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc0, 0xae, 0xff})));
|
||||||
CHECK(_ == "\xc3\xa9\xc0\xae");
|
CHECK(_ == "\xc3\xa9\xc0\xae");
|
||||||
CHECK_THROWS_AS(_.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(_.dump()), json::type_error&);
|
||||||
|
|
||||||
// valid multi-byte chunks are accepted
|
// valid multi-byte chunks are accepted
|
||||||
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
CHECK(json::from_cbor(std::vector<uint8_t>({0x7f, 0x62, 0xc3, 0xa9, 0x62, 0xc3, 0xb6, 0xff})) == "\xc3\xa9\xc3\xb6");
|
||||||
@@ -2390,6 +2390,7 @@ TEST_CASE("issue #5405 - array reserve for definite-length CBOR arrays")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION) // corpus values that do not survive the round trip are skipped by catching the exception
|
||||||
TEST_CASE("CBOR round-trip invariants")
|
TEST_CASE("CBOR round-trip invariants")
|
||||||
{
|
{
|
||||||
// This checks what the parse_cbor_fuzzer driver checks (see
|
// This checks what the parse_cbor_fuzzer driver checks (see
|
||||||
@@ -2422,6 +2423,7 @@ TEST_CASE("CBOR round-trip invariants")
|
|||||||
CHECK(json::to_cbor(j2) == vec);
|
CHECK(json::to_cbor(j2) == vec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("CBOR roundtrips" * doctest::skip())
|
TEST_CASE("CBOR roundtrips" * doctest::skip())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2704,9 +2704,9 @@ TEST_CASE("diagnostic positions: value lifetime, input adapters, and SAX")
|
|||||||
CHECK(b["b"].end_pos() == nested_end);
|
CHECK(b["b"].end_pos() == nested_end);
|
||||||
|
|
||||||
// the moved-from value is reset to a null and reports npos
|
// the moved-from value is reset to a null and reports npos
|
||||||
CHECK(a.is_null()); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move)
|
CHECK(a.is_null()); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move)
|
||||||
CHECK(a.start_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move)
|
CHECK(a.start_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move)
|
||||||
CHECK(a.end_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,clang-analyzer-cplusplus.Move)
|
CHECK(a.end_pos() == std::string::npos); // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved,clang-analyzer-cplusplus.Move)
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("swap() exchanges positions along with values")
|
SECTION("swap() exchanges positions along with values")
|
||||||
|
|||||||
@@ -969,7 +969,7 @@ TEST_CASE("copying an object preserves its comparator's state")
|
|||||||
|
|
||||||
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
||||||
{
|
{
|
||||||
CAPTURE(depth);
|
CAPTURE(depth)
|
||||||
|
|
||||||
key_case_json original = object;
|
key_case_json original = object;
|
||||||
for (std::size_t i = 0; i < depth; ++i)
|
for (std::size_t i = 0; i < depth; ++i)
|
||||||
@@ -1138,7 +1138,7 @@ TEST_CASE("operator<=> of binary values with a different subtype does not depend
|
|||||||
// and must still agree with the levels that do
|
// and must still agree with the levels that do
|
||||||
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
for (const std::size_t depth : std::vector<std::size_t> {0, 127, 128, 200})
|
||||||
{
|
{
|
||||||
CAPTURE(depth);
|
CAPTURE(depth)
|
||||||
const json x = deep(a, depth);
|
const json x = deep(a, depth);
|
||||||
const json y = deep(b, depth);
|
const json y = deep(b, depth);
|
||||||
CHECK((x <=> y) == std::partial_ordering::less); // *NOPAD*
|
CHECK((x <=> y) == std::partial_ordering::less); // *NOPAD*
|
||||||
|
|||||||
@@ -15,11 +15,6 @@
|
|||||||
|
|
||||||
#include "doctest_compatibility.h"
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
// skip tests if JSON_DisableEnumSerialization=ON (#4384)
|
|
||||||
#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1)
|
|
||||||
#define SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define JSON_TESTS_PRIVATE
|
#define JSON_TESTS_PRIVATE
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using nlohmann::json;
|
using nlohmann::json;
|
||||||
@@ -1259,808 +1254,8 @@ TEST_CASE("value conversion")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SECTION("get a binary value (explicit)")
|
|
||||||
{
|
|
||||||
json::binary_t const n_reference{{1, 2, 3}};
|
|
||||||
json j(n_reference);
|
|
||||||
|
|
||||||
SECTION("binary_t")
|
|
||||||
{
|
|
||||||
json::binary_t const b = j.get<json::binary_t>();
|
|
||||||
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("get_binary()")
|
|
||||||
{
|
|
||||||
SECTION("non-const")
|
|
||||||
{
|
|
||||||
auto& b = j.get_binary();
|
|
||||||
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("non-const")
|
|
||||||
{
|
|
||||||
const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization)
|
|
||||||
const auto& b = j_const.get_binary();
|
|
||||||
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("exception in case of a non-string type")
|
|
||||||
{
|
|
||||||
json j_null(json::value_t::null);
|
|
||||||
json j_object(json::value_t::object);
|
|
||||||
json j_array(json::value_t::array);
|
|
||||||
json j_string(json::value_t::string);
|
|
||||||
json j_boolean(json::value_t::boolean);
|
|
||||||
const json j_null_const(json::value_t::null);
|
|
||||||
const json j_object_const(json::value_t::object);
|
|
||||||
const json j_array_const(json::value_t::array);
|
|
||||||
const json j_string_const(json::value_t::string);
|
|
||||||
const json j_boolean_const(json::value_t::boolean);
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(j_null.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is null",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_object.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is object",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_array.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is array",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_string.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is string",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_boolean.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is boolean",
|
|
||||||
json::type_error&);
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(j_null_const.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is null",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_object_const.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is object",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_array_const.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is array",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_string_const.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is string",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_boolean_const.get<json::binary_t>(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is boolean",
|
|
||||||
json::type_error&);
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(j_null.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is null",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_object.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is object",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_array.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is array",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_string.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is string",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_boolean.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is boolean",
|
|
||||||
json::type_error&);
|
|
||||||
|
|
||||||
CHECK_THROWS_WITH_AS(j_null_const.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is null",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_object_const.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is object",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_array_const.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is array",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_string_const.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is string",
|
|
||||||
json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(),
|
|
||||||
"[json.exception.type_error.302] type must be binary, but is boolean",
|
|
||||||
json::type_error&);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
||||||
SECTION("get a binary value (implicit)")
|
|
||||||
{
|
|
||||||
json::binary_t const n_reference{{1, 2, 3}};
|
|
||||||
json const j(n_reference);
|
|
||||||
|
|
||||||
SECTION("binary_t")
|
|
||||||
{
|
|
||||||
json::binary_t const b = j;
|
|
||||||
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
|
||||||
SECTION("get an enum")
|
|
||||||
{
|
|
||||||
enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class)
|
|
||||||
enum class cpp_enum { value_1, value_2 };
|
|
||||||
|
|
||||||
CHECK(json(value_1).get<c_enum>() == value_1);
|
|
||||||
CHECK(json(cpp_enum::value_1).get<cpp_enum>() == cpp_enum::value_1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("get an enum with underlying type bool (#5671)")
|
|
||||||
{
|
|
||||||
enum class bool_enum : bool { off, on };
|
|
||||||
|
|
||||||
CHECK(json(bool_enum::off).get<bool_enum>() == bool_enum::off);
|
|
||||||
CHECK(json(bool_enum::on).get<bool_enum>() == bool_enum::on);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SECTION("more involved conversions")
|
|
||||||
{
|
|
||||||
SECTION("object-like STL containers")
|
|
||||||
{
|
|
||||||
json const j1 = {{"one", 1}, {"two", 2}, {"three", 3}};
|
|
||||||
json const j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}};
|
|
||||||
json const j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}};
|
|
||||||
json const j4 = {{"one", true}, {"two", false}, {"three", true}};
|
|
||||||
json const j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}};
|
|
||||||
|
|
||||||
SECTION("std::map")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::map<std::string, int>>() == (std::map<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
|
||||||
CHECK(j2.get<std::map<std::string, unsigned int>>() == (std::map<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
|
||||||
CHECK(j3.get<std::map<std::string, double>>() == (std::map<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
|
||||||
CHECK(j4.get<std::map<std::string, bool>>() == (std::map<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
|
||||||
CHECK(j5.get<std::map<std::string, std::string>>() == (std::map<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::unordered_map")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::unordered_map<std::string, int>>() == (std::unordered_map<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
|
||||||
CHECK(j2.get<std::unordered_map<std::string, unsigned int>>() == (std::unordered_map<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
|
||||||
CHECK(j3.get<std::unordered_map<std::string, double>>() == (std::unordered_map<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
|
||||||
CHECK(j4.get<std::unordered_map<std::string, bool>>() == (std::unordered_map<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
|
||||||
const auto m5 = j5.get<std::unordered_map<std::string, std::string>>();
|
|
||||||
CHECK(m5 == (std::unordered_map<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
|
||||||
CHECK(m5.at("one") == "eins");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("reserve is called on containers that support it (#5406)")
|
|
||||||
{
|
|
||||||
// build a larger object so that a missing/incorrect reserve()
|
|
||||||
// call would be more likely to corrupt or drop elements
|
|
||||||
json j_large;
|
|
||||||
for (int i = 0; i < 100; ++i)
|
|
||||||
{
|
|
||||||
j_large[std::to_string(i)] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::unordered_map (supports reserve)")
|
|
||||||
{
|
|
||||||
const auto m = j_large.get<std::unordered_map<std::string, int>>();
|
|
||||||
CHECK(m.size() == 100);
|
|
||||||
for (int i = 0; i < 100; ++i)
|
|
||||||
{
|
|
||||||
CHECK(m.at(std::to_string(i)) == i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::map (no reserve, fallback path)")
|
|
||||||
{
|
|
||||||
const auto m = j_large.get<std::map<std::string, int>>();
|
|
||||||
CHECK(m.size() == 100);
|
|
||||||
for (int i = 0; i < 100; ++i)
|
|
||||||
{
|
|
||||||
CHECK(m.at(std::to_string(i)) == i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::multimap")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::multimap<std::string, int>>() == (std::multimap<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
|
||||||
CHECK(j2.get<std::multimap<std::string, unsigned int>>() == (std::multimap<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
|
||||||
CHECK(j3.get<std::multimap<std::string, double>>() == (std::multimap<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
|
||||||
CHECK(j4.get<std::multimap<std::string, bool>>() == (std::multimap<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
|
||||||
const auto m5 = j5.get<std::multimap<std::string, std::string>>();
|
|
||||||
CHECK(m5 == (std::multimap<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
|
||||||
CHECK(m5.find("one")->second == "eins");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::unordered_multimap")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::unordered_multimap<std::string, int>>() == (std::unordered_multimap<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
|
||||||
CHECK(j2.get<std::unordered_multimap<std::string, unsigned int>>() == (std::unordered_multimap<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
|
||||||
CHECK(j3.get<std::unordered_multimap<std::string, double>>() == (std::unordered_multimap<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
|
||||||
CHECK(j4.get<std::unordered_multimap<std::string, bool>>() == (std::unordered_multimap<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
|
||||||
const auto m5 = j5.get<std::unordered_multimap<std::string, std::string>>();
|
|
||||||
CHECK(m5 == (std::unordered_multimap<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
|
||||||
CHECK(m5.find("one")->second == "eins");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("exception in case of a non-object type")
|
|
||||||
{
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::map<std::string, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be object, but is null", json::type_error&);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("array-like STL containers")
|
|
||||||
{
|
|
||||||
json const j1 = {1, 2, 3, 4};
|
|
||||||
json const j2 = {1u, 2u, 3u, 4u};
|
|
||||||
json const j3 = {1.2, 2.3, 3.4, 4.5};
|
|
||||||
json const j4 = {true, false, true};
|
|
||||||
json const j5 = {"one", "two", "three"};
|
|
||||||
|
|
||||||
SECTION("std::list")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::list<int>>() == (std::list<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::list<unsigned int>>() == (std::list<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::list<double>>() == (std::list<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::list<bool>>() == (std::list<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::list<std::string>>() == (std::list<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::forward_list")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::forward_list<int>>() == (std::forward_list<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::forward_list<unsigned int>>() == (std::forward_list<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::forward_list<double>>() == (std::forward_list<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::forward_list<bool>>() == (std::forward_list<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::forward_list<std::string>>() == (std::forward_list<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::array")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::array<int, 4>>() == (std::array<int, 4> {{1, 2, 3, 4}}));
|
|
||||||
// only the first 3 elements of j2 are converted, since the target array is smaller
|
|
||||||
CHECK(j2.get<std::array<unsigned int, 3>>() == (std::array<unsigned int, 3> {{1u, 2u, 3u}}));
|
|
||||||
CHECK(j3.get<std::array<double, 4>>() == (std::array<double, 4> {{1.2, 2.3, 3.4, 4.5}}));
|
|
||||||
CHECK(j4.get<std::array<bool, 3>>() == (std::array<bool, 3> {{true, false, true}}));
|
|
||||||
CHECK(j5.get<std::array<std::string, 3>>() == (std::array<std::string, 3> {{"one", "two", "three"}}));
|
|
||||||
|
|
||||||
SECTION("std::array is larger than JSON")
|
|
||||||
{
|
|
||||||
std::array<int, 6> arr6 = {{1, 2, 3, 4, 5, 6}};
|
|
||||||
CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] "
|
|
||||||
"array index 4 is out of range", json::out_of_range&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::array is smaller than JSON")
|
|
||||||
{
|
|
||||||
std::array<int, 2> arr2 = {{8, 9}};
|
|
||||||
j1.get_to(arr2);
|
|
||||||
CHECK(arr2[0] == 1);
|
|
||||||
CHECK(arr2[1] == 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::valarray")
|
|
||||||
{
|
|
||||||
// valarray has no operator== that returns bool, so compare via a vector copy
|
|
||||||
const auto v1 = j1.get<std::valarray<int>>();
|
|
||||||
CHECK((std::vector<int>(std::begin(v1), std::end(v1)) == std::vector<int> {1, 2, 3, 4}));
|
|
||||||
const auto v2 = j2.get<std::valarray<unsigned int>>();
|
|
||||||
CHECK((std::vector<unsigned int>(std::begin(v2), std::end(v2)) == std::vector<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
const auto v3 = j3.get<std::valarray<double>>();
|
|
||||||
CHECK((std::vector<double>(std::begin(v3), std::end(v3)) == std::vector<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
const auto v4 = j4.get<std::valarray<bool>>();
|
|
||||||
CHECK((std::vector<bool>(std::begin(v4), std::end(v4)) == std::vector<bool> {true, false, true}));
|
|
||||||
const auto v5 = j5.get<std::valarray<std::string>>();
|
|
||||||
CHECK((std::vector<std::string>(std::begin(v5), std::end(v5)) == std::vector<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::vector")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::vector<int>>() == (std::vector<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::vector<unsigned int>>() == (std::vector<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::vector<double>>() == (std::vector<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::vector<bool>>() == (std::vector<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::vector<std::string>>() == (std::vector<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::deque")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::deque<int>>() == (std::deque<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::deque<unsigned int>>() == (std::deque<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::deque<double>>() == (std::deque<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::deque<bool>>() == (std::deque<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::deque<std::string>>() == (std::deque<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::set")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::set<int>>() == (std::set<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::set<unsigned int>>() == (std::set<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::set<double>>() == (std::set<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::set<bool>>() == (std::set<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::set<std::string>>() == (std::set<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::unordered_set")
|
|
||||||
{
|
|
||||||
CHECK(j1.get<std::unordered_set<int>>() == (std::unordered_set<int> {1, 2, 3, 4}));
|
|
||||||
CHECK(j2.get<std::unordered_set<unsigned int>>() == (std::unordered_set<unsigned int> {1u, 2u, 3u, 4u}));
|
|
||||||
CHECK(j3.get<std::unordered_set<double>>() == (std::unordered_set<double> {1.2, 2.3, 3.4, 4.5}));
|
|
||||||
CHECK(j4.get<std::unordered_set<bool>>() == (std::unordered_set<bool> {true, false, true}));
|
|
||||||
CHECK(j5.get<std::unordered_set<std::string>>() == (std::unordered_set<std::string> {"one", "two", "three"}));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::map (array of pairs)")
|
|
||||||
{
|
|
||||||
const std::map<int, int> m{{0, 1}, {1, 2}, {2, 3}};
|
|
||||||
json const j6 = m;
|
|
||||||
|
|
||||||
auto m2 = j6.get<std::map<int, int>>();
|
|
||||||
CHECK(m == m2);
|
|
||||||
|
|
||||||
json const j7 = {0, 1, 2, 3};
|
|
||||||
json const j8 = 2;
|
|
||||||
#if JSON_DIAGNOSTICS
|
|
||||||
CHECK_THROWS_WITH_AS((j7.get<std::map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] (/0) type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
#else
|
|
||||||
CHECK_THROWS_WITH_AS((j7.get<std::map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
#endif
|
|
||||||
CHECK_THROWS_WITH_AS((j8.get<std::map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
|
|
||||||
SECTION("superfluous entries")
|
|
||||||
{
|
|
||||||
json const j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
|
|
||||||
m2 = j9.get<std::map<int, int>>();
|
|
||||||
CHECK(m == m2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("std::unordered_map (array of pairs)")
|
|
||||||
{
|
|
||||||
const std::unordered_map<int, int> m{{0, 1}, {1, 2}, {2, 3}};
|
|
||||||
json const j6 = m;
|
|
||||||
|
|
||||||
auto m2 = j6.get<std::unordered_map<int, int>>();
|
|
||||||
CHECK(m == m2);
|
|
||||||
|
|
||||||
json const j7 = {0, 1, 2, 3};
|
|
||||||
json const j8 = 2;
|
|
||||||
#if JSON_DIAGNOSTICS
|
|
||||||
CHECK_THROWS_WITH_AS((j7.get<std::unordered_map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] (/0) type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
#else
|
|
||||||
CHECK_THROWS_WITH_AS((j7.get<std::unordered_map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
#endif
|
|
||||||
CHECK_THROWS_WITH_AS((j8.get<std::unordered_map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, "
|
|
||||||
"but is number", json::type_error&);
|
|
||||||
|
|
||||||
SECTION("superfluous entries")
|
|
||||||
{
|
|
||||||
json const j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
|
|
||||||
m2 = j9.get<std::unordered_map<int, int>>();
|
|
||||||
CHECK(m == m2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("exception in case of a non-object type")
|
|
||||||
{
|
|
||||||
// does type really must be an array? or it rather must not be null?
|
|
||||||
// that's what I thought when other test like this one broke
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::list<int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::vector<int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::vector<json>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::list<json>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::valarray<int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(
|
|
||||||
(json().get<std::map<int, int>>()),
|
|
||||||
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class cards {kreuz, pik, herz, karo};
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(cards,
|
|
||||||
{
|
|
||||||
{cards::kreuz, "kreuz"},
|
|
||||||
{cards::pik, "pik"},
|
|
||||||
{cards::pik, "puk"}, // second entry for cards::puk; will not be used
|
|
||||||
{cards::herz, "herz"},
|
|
||||||
{cards::karo, "karo"}
|
|
||||||
})
|
|
||||||
|
|
||||||
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
|
||||||
{
|
|
||||||
TS_STOPPED,
|
|
||||||
TS_RUNNING,
|
|
||||||
TS_COMPLETED,
|
|
||||||
TS_INVALID = -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
|
|
||||||
{
|
|
||||||
{TS_INVALID, nullptr},
|
|
||||||
{TS_STOPPED, "stopped"},
|
|
||||||
{TS_RUNNING, "running"},
|
|
||||||
{TS_COMPLETED, "completed"},
|
|
||||||
})
|
|
||||||
|
|
||||||
TEST_CASE("JSON to enum mapping")
|
|
||||||
{
|
|
||||||
SECTION("enum class")
|
|
||||||
{
|
|
||||||
// enum -> json
|
|
||||||
CHECK(json(cards::kreuz) == "kreuz");
|
|
||||||
CHECK(json(cards::pik) == "pik");
|
|
||||||
CHECK(json(cards::herz) == "herz");
|
|
||||||
CHECK(json(cards::karo) == "karo");
|
|
||||||
|
|
||||||
// json -> enum
|
|
||||||
CHECK(cards::kreuz == json("kreuz"));
|
|
||||||
CHECK(cards::pik == json("pik"));
|
|
||||||
CHECK(cards::herz == json("herz"));
|
|
||||||
CHECK(cards::karo == json("karo"));
|
|
||||||
|
|
||||||
// invalid json -> first enum
|
|
||||||
CHECK(cards::kreuz == json("what?").get<cards>());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("traditional enum")
|
|
||||||
{
|
|
||||||
// enum -> json
|
|
||||||
CHECK(json(TS_STOPPED) == "stopped");
|
|
||||||
CHECK(json(TS_RUNNING) == "running");
|
|
||||||
CHECK(json(TS_COMPLETED) == "completed");
|
|
||||||
CHECK(json(TS_INVALID) == json());
|
|
||||||
|
|
||||||
// json -> enum
|
|
||||||
CHECK(TS_STOPPED == json("stopped"));
|
|
||||||
CHECK(TS_RUNNING == json("running"));
|
|
||||||
CHECK(TS_COMPLETED == json("completed"));
|
|
||||||
CHECK(TS_INVALID == json());
|
|
||||||
|
|
||||||
// invalid json -> first enum
|
|
||||||
CHECK(TS_INVALID == json("what?").get<TaskState>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards,
|
|
||||||
{
|
|
||||||
{strict_cards::kreuz, "kreuz"},
|
|
||||||
{strict_cards::pik, "pik"},
|
|
||||||
{strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used
|
|
||||||
{strict_cards::herz, "herz"},
|
|
||||||
{strict_cards::karo, "karo"}
|
|
||||||
})
|
|
||||||
|
|
||||||
enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
|
||||||
{
|
|
||||||
STRICT_TS_STOPPED,
|
|
||||||
STRICT_TS_RUNNING,
|
|
||||||
STRICT_TS_COMPLETED,
|
|
||||||
STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping
|
|
||||||
STRICT_TS_INVALID = -1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState,
|
|
||||||
{
|
|
||||||
{STRICT_TS_INVALID, nullptr},
|
|
||||||
{STRICT_TS_STOPPED, "stopped"},
|
|
||||||
{STRICT_TS_RUNNING, "running"},
|
|
||||||
{STRICT_TS_COMPLETED, "completed"},
|
|
||||||
})
|
|
||||||
|
|
||||||
// regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on
|
|
||||||
// unqualified lookup of a helper name that a user's own namespace may also declare
|
|
||||||
namespace ns_with_colliding_name
|
|
||||||
{
|
|
||||||
// NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name
|
|
||||||
inline void templated_json_throw(int /*unused*/) {}
|
|
||||||
|
|
||||||
enum class colliding_enum { a, b };
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
|
||||||
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum,
|
|
||||||
{
|
|
||||||
{colliding_enum::a, "a"},
|
|
||||||
{colliding_enum::b, "b"}
|
|
||||||
})
|
|
||||||
} // namespace ns_with_colliding_name
|
|
||||||
|
|
||||||
TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name")
|
|
||||||
{
|
|
||||||
using ns_with_colliding_name::colliding_enum;
|
|
||||||
|
|
||||||
CHECK(json(colliding_enum::a) == "a");
|
|
||||||
CHECK(colliding_enum::b == json("b"));
|
|
||||||
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json("nope").get<colliding_enum>(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Strict JSON to enum mapping")
|
|
||||||
{
|
|
||||||
SECTION("enum class")
|
|
||||||
{
|
|
||||||
// enum -> json
|
|
||||||
CHECK(json(strict_cards::kreuz) == "kreuz");
|
|
||||||
CHECK(json(strict_cards::pik) == "pik");
|
|
||||||
CHECK(json(strict_cards::herz) == "herz");
|
|
||||||
CHECK(json(strict_cards::karo) == "karo");
|
|
||||||
|
|
||||||
// json -> enum
|
|
||||||
CHECK(json("kreuz").get<strict_cards>() == strict_cards::kreuz);
|
|
||||||
CHECK(json("pik").get<strict_cards>() == strict_cards::pik);
|
|
||||||
CHECK(json("herz").get<strict_cards>() == strict_cards::herz);
|
|
||||||
CHECK(json("karo").get<strict_cards>() == strict_cards::karo);
|
|
||||||
|
|
||||||
// comparison of enum and json
|
|
||||||
CHECK(strict_cards::kreuz == json("kreuz"));
|
|
||||||
CHECK(strict_cards::pik == json("pik"));
|
|
||||||
CHECK(strict_cards::herz == json("herz"));
|
|
||||||
CHECK(strict_cards::karo == json("karo"));
|
|
||||||
|
|
||||||
// invalid json -> exception thrown
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json("what?").get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&);
|
|
||||||
|
|
||||||
// conversion of unmapped enum -> exception thrown
|
|
||||||
CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
|
||||||
|
|
||||||
// comparing an unmapped enum with json throws the same exception
|
|
||||||
// (the scalar comparison operators used to be noexcept, so this
|
|
||||||
// called std::terminate)
|
|
||||||
CHECK_THROWS_WITH_AS(static_cast<void>(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
|
||||||
CHECK_THROWS_WITH_AS(static_cast<void>(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
|
||||||
|
|
||||||
// invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the
|
|
||||||
// message (regression test for #5667); such strings can reach get<Enum>() unvalidated,
|
|
||||||
// e.g. from from_cbor()/from_msgpack() (#5529)
|
|
||||||
const json j_invalid_utf8 = "\xFF";
|
|
||||||
CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("traditional enum")
|
|
||||||
{
|
|
||||||
// enum -> json
|
|
||||||
CHECK(json(STRICT_TS_STOPPED) == "stopped");
|
|
||||||
CHECK(json(STRICT_TS_RUNNING) == "running");
|
|
||||||
CHECK(json(STRICT_TS_COMPLETED) == "completed");
|
|
||||||
CHECK(json(STRICT_TS_INVALID) == json());
|
|
||||||
|
|
||||||
// json -> enum
|
|
||||||
CHECK(json("stopped").get<StrictTaskState>() == STRICT_TS_STOPPED);
|
|
||||||
CHECK(json("running").get<StrictTaskState>() == STRICT_TS_RUNNING);
|
|
||||||
CHECK(json("completed").get<StrictTaskState>() == STRICT_TS_COMPLETED);
|
|
||||||
CHECK(json().get<StrictTaskState>() == STRICT_TS_INVALID);
|
|
||||||
|
|
||||||
// comparison of enum and json
|
|
||||||
CHECK(STRICT_TS_STOPPED == json("stopped"));
|
|
||||||
CHECK(STRICT_TS_RUNNING == json("running"));
|
|
||||||
CHECK(STRICT_TS_COMPLETED == json("completed"));
|
|
||||||
CHECK(STRICT_TS_INVALID == json());
|
|
||||||
|
|
||||||
// invalid json -> exception thrown
|
|
||||||
json _;
|
|
||||||
CHECK_THROWS_WITH_AS(_ = json("what?").get<StrictTaskState>(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&);
|
|
||||||
|
|
||||||
// conversion of unmapped enum -> exception thrown
|
|
||||||
CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
|
|
||||||
|
|
||||||
// comparing an unmapped enum with json throws the same exception
|
|
||||||
CHECK_THROWS_WITH_AS(static_cast<void>(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
|
||||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
TEST_CASE("std::filesystem::path")
|
|
||||||
{
|
|
||||||
SECTION("ascii")
|
|
||||||
{
|
|
||||||
json const j_string = "Path";
|
|
||||||
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
|
|
||||||
json const j_path = p;
|
|
||||||
|
|
||||||
CHECK(j_path.template get<std::string>() ==
|
|
||||||
j_string.template get<std::string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("utf-8")
|
|
||||||
{
|
|
||||||
json const j_string = "P\xc4\x9b\xc5\xa1ina";
|
|
||||||
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
|
|
||||||
json const j_path = p;
|
|
||||||
|
|
||||||
CHECK(j_path.template get<std::string>() ==
|
|
||||||
j_string.template get<std::string>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// the ADL to_json overload for std::u8string only exists under the same guard
|
|
||||||
// as std::filesystem::path support (it is otherwise only reached indirectly,
|
|
||||||
// via std::filesystem::path::u8string()) -- mirror both #if conditions from
|
|
||||||
// include/nlohmann/detail/conversions/to_json.hpp exactly
|
|
||||||
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
||||||
#if defined(__cpp_lib_char8_t)
|
|
||||||
TEST_CASE("std::u8string")
|
|
||||||
{
|
|
||||||
SECTION("ascii")
|
|
||||||
{
|
|
||||||
const std::u8string s = u8"Path";
|
|
||||||
json const j = s;
|
|
||||||
|
|
||||||
CHECK(j.template get<std::string>() == "Path");
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("utf-8")
|
|
||||||
{
|
|
||||||
// use \u universal-character-names (rather than raw \x byte escapes
|
|
||||||
// or literal non-ASCII source bytes) to compose the multi-byte UTF-8
|
|
||||||
// encoding -- MSVC treats \x escapes used that way inside a u8
|
|
||||||
// literal as a nonstandard extension (warning C5321), which some of
|
|
||||||
// our CI configs promote to an error; \u is portable and produces
|
|
||||||
// the exact same encoded bytes without depending on the source
|
|
||||||
// file's encoding
|
|
||||||
const std::u8string s = u8"P\u011B\u0161ina";
|
|
||||||
json const j = s;
|
|
||||||
|
|
||||||
CHECK(j.template get<std::string>() == "P\xc4\x9b\xc5\xa1ina");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
// a type whose to_json reports an error by throwing, used below to check that
|
|
||||||
// converting a std::optional<T> to JSON propagates an exception thrown while
|
|
||||||
// converting its contained value instead of calling std::terminate (#5642)
|
|
||||||
struct throwing_to_json_type {};
|
|
||||||
|
|
||||||
[[noreturn]] void to_json(json& /*unused*/, const throwing_to_json_type& /*unused*/)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("cannot serialize throwing_to_json_type");
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST_CASE("std::optional")
|
|
||||||
{
|
|
||||||
SECTION("null")
|
|
||||||
{
|
|
||||||
const json j_null;
|
|
||||||
const std::optional<std::string> opt_null;
|
|
||||||
|
|
||||||
CHECK(json(opt_null) == j_null);
|
|
||||||
CHECK(j_null.get<std::optional<std::string>>() == std::nullopt);
|
|
||||||
|
|
||||||
// Constructing std::optional<T> directly from JSON null throws because
|
|
||||||
// std::optional's own converting constructor is chosen over basic_json's
|
|
||||||
// operator T(). This is a language-level limitation (std::optional<T> is
|
|
||||||
// constructible from T, and T is constructible from basic_json via the
|
|
||||||
// operator); there is no SFINAE path that distinguishes "call from inside
|
|
||||||
// std::optional's constructor" from "direct call". Use get<std::optional<T>>()
|
|
||||||
// or get_to() instead for correct null handling. See #4864 and #5246.
|
|
||||||
CHECK_THROWS_WITH_AS(std::optional<std::string>(j_null),
|
|
||||||
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
|
||||||
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
|
|
||||||
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
|
||||||
|
|
||||||
// Assignment goes through the same overload resolution as direct
|
|
||||||
// construction, so it throws for the same reason. This relies on
|
|
||||||
// basic_json's implicit conversion operator, so it only applies
|
|
||||||
// when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default).
|
|
||||||
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
||||||
std::optional<std::string> opt_assign;
|
|
||||||
CHECK_THROWS_WITH_AS(opt_assign = j_null,
|
|
||||||
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// get_to() is the correct way to obtain std::nullopt from a JSON null.
|
|
||||||
std::optional<std::string> opt_get_to = "placeholder";
|
|
||||||
j_null.get_to(opt_get_to);
|
|
||||||
CHECK(opt_get_to == std::nullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("string")
|
|
||||||
{
|
|
||||||
json j_string = "string";
|
|
||||||
std::optional<std::string> opt_string = "string";
|
|
||||||
|
|
||||||
CHECK(json(opt_string) == j_string);
|
|
||||||
CHECK(std::optional<std::string>(j_string) == opt_string);
|
|
||||||
// false positive: Infer attributes the destruction of the temporaries above to opt_string
|
|
||||||
// @infer-ignore USE_AFTER_DELETE
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("bool")
|
|
||||||
{
|
|
||||||
json j_bool = true;
|
|
||||||
std::optional<bool> opt_bool = true;
|
|
||||||
|
|
||||||
CHECK(json(opt_bool) == j_bool);
|
|
||||||
CHECK(std::optional<bool>(j_bool) == opt_bool);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("number")
|
|
||||||
{
|
|
||||||
json j_number = 1;
|
|
||||||
std::optional<int> opt_int = 1;
|
|
||||||
|
|
||||||
CHECK(json(opt_int) == j_number);
|
|
||||||
CHECK(j_number.get<std::optional<int>>() == opt_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("array")
|
|
||||||
{
|
|
||||||
json j_array = {1, 2, nullptr};
|
|
||||||
std::vector<std::optional<int>> opt_array = {{1, 2, std::nullopt}};
|
|
||||||
|
|
||||||
CHECK(json(opt_array) == j_array);
|
|
||||||
CHECK(j_array.get<std::vector<std::optional<int>>>() == opt_array);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("object")
|
|
||||||
{
|
|
||||||
json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}};
|
|
||||||
std::map<std::string, std::optional<int>> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}};
|
|
||||||
|
|
||||||
CHECK(json(opt_object) == j_object);
|
|
||||||
CHECK(std::map<std::string, std::optional<int>>(j_object) == opt_object);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(JSON_NOEXCEPTION)
|
|
||||||
SECTION("exception from contained value's to_json propagates (#5642)")
|
|
||||||
{
|
|
||||||
// to_json(BasicJsonType&, const std::optional<T>&) must not be
|
|
||||||
// noexcept: it calls T's to_json, which may throw (a user-defined
|
|
||||||
// to_json that reports an error, or std::bad_alloc for T =
|
|
||||||
// std::string/vector/json). Before the fix, this called
|
|
||||||
// std::terminate() instead of letting the exception propagate.
|
|
||||||
const std::optional<throwing_to_json_type> opt = throwing_to_json_type{};
|
|
||||||
CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&);
|
|
||||||
|
|
||||||
// the conversion is noexcept exactly when converting the contained value is
|
|
||||||
static_assert(!std::is_nothrow_constructible<json, const std::optional<throwing_to_json_type>&>::value);
|
|
||||||
static_assert(std::is_nothrow_constructible<json, const std::optional<int>&>::value);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
#undef JSON_HAS_CPP_17
|
#undef JSON_HAS_CPP_17
|
||||||
#endif
|
#endif
|
||||||
@@ -0,0 +1,868 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// cmake/test.cmake selects the C++ standard versions with which to build a
|
||||||
|
// unit test based on the presence of JSON_HAS_CPP_<VERSION> macros.
|
||||||
|
// When using macros that are only defined for particular versions of the standard
|
||||||
|
// (e.g., JSON_HAS_FILESYSTEM for C++17 and up), please mention the corresponding
|
||||||
|
// version macro in a comment close by, like this:
|
||||||
|
// JSON_HAS_CPP_<VERSION> (do not remove; see note at top of file)
|
||||||
|
|
||||||
|
#include "doctest_compatibility.h"
|
||||||
|
|
||||||
|
// skip tests if JSON_DisableEnumSerialization=ON (#4384)
|
||||||
|
#if defined(JSON_DISABLE_ENUM_SERIALIZATION) && (JSON_DISABLE_ENUM_SERIALIZATION == 1)
|
||||||
|
#define SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JSON_TESTS_PRIVATE
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
using nlohmann::json;
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
#include <forward_list>
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <valarray>
|
||||||
|
|
||||||
|
// NLOHMANN_JSON_SERIALIZE_ENUM uses a static std::pair
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")
|
||||||
|
|
||||||
|
#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464
|
||||||
|
#define JSON_HAS_CPP_17
|
||||||
|
#define JSON_HAS_CPP_14
|
||||||
|
#elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1)
|
||||||
|
#define JSON_HAS_CPP_14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
#if __has_include(<optional>)
|
||||||
|
#include <optional>
|
||||||
|
#elif __has_include(<experimental/optional>)
|
||||||
|
#include <experimental/optional>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(JSON_HAS_CPP_17)
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("value conversion")
|
||||||
|
{
|
||||||
|
SECTION("get a binary value (explicit)")
|
||||||
|
{
|
||||||
|
json::binary_t const n_reference{{1, 2, 3}};
|
||||||
|
json j(n_reference);
|
||||||
|
|
||||||
|
SECTION("binary_t")
|
||||||
|
{
|
||||||
|
json::binary_t const b = j.get<json::binary_t>();
|
||||||
|
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("get_binary()")
|
||||||
|
{
|
||||||
|
SECTION("non-const")
|
||||||
|
{
|
||||||
|
auto& b = j.get_binary();
|
||||||
|
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("non-const")
|
||||||
|
{
|
||||||
|
const json j_const = j; // NOLINT(performance-unnecessary-copy-initialization)
|
||||||
|
const auto& b = j_const.get_binary();
|
||||||
|
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("exception in case of a non-string type")
|
||||||
|
{
|
||||||
|
json j_null(json::value_t::null);
|
||||||
|
json j_object(json::value_t::object);
|
||||||
|
json j_array(json::value_t::array);
|
||||||
|
json j_string(json::value_t::string);
|
||||||
|
json j_boolean(json::value_t::boolean);
|
||||||
|
const json j_null_const(json::value_t::null);
|
||||||
|
const json j_object_const(json::value_t::object);
|
||||||
|
const json j_array_const(json::value_t::array);
|
||||||
|
const json j_string_const(json::value_t::string);
|
||||||
|
const json j_boolean_const(json::value_t::boolean);
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(j_null.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is null",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_object.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is object",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_array.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is array",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_string.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is string",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_boolean.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is boolean",
|
||||||
|
json::type_error&);
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(j_null_const.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is null",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_object_const.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is object",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_array_const.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is array",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_string_const.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is string",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_boolean_const.get<json::binary_t>(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is boolean",
|
||||||
|
json::type_error&);
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(j_null.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is null",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_object.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is object",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_array.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is array",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_string.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is string",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_boolean.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is boolean",
|
||||||
|
json::type_error&);
|
||||||
|
|
||||||
|
CHECK_THROWS_WITH_AS(j_null_const.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is null",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_object_const.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is object",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_array_const.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is array",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_string_const.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is string",
|
||||||
|
json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(j_boolean_const.get_binary(),
|
||||||
|
"[json.exception.type_error.302] type must be binary, but is boolean",
|
||||||
|
json::type_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||||
|
SECTION("get a binary value (implicit)")
|
||||||
|
{
|
||||||
|
json::binary_t const n_reference{{1, 2, 3}};
|
||||||
|
json const j(n_reference);
|
||||||
|
|
||||||
|
SECTION("binary_t")
|
||||||
|
{
|
||||||
|
json::binary_t const b = j;
|
||||||
|
CHECK(*json(b).m_data.m_value.binary == *j.m_data.m_value.binary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
||||||
|
SECTION("get an enum")
|
||||||
|
{
|
||||||
|
enum c_enum { value_1, value_2 }; // NOLINT(cppcoreguidelines-use-enum-class)
|
||||||
|
enum class cpp_enum { value_1, value_2 };
|
||||||
|
|
||||||
|
CHECK(json(value_1).get<c_enum>() == value_1);
|
||||||
|
CHECK(json(cpp_enum::value_1).get<cpp_enum>() == cpp_enum::value_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("get an enum with underlying type bool (#5671)")
|
||||||
|
{
|
||||||
|
enum class bool_enum : bool { off, on };
|
||||||
|
|
||||||
|
CHECK(json(bool_enum::off).get<bool_enum>() == bool_enum::off);
|
||||||
|
CHECK(json(bool_enum::on).get<bool_enum>() == bool_enum::on);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SECTION("more involved conversions")
|
||||||
|
{
|
||||||
|
SECTION("object-like STL containers")
|
||||||
|
{
|
||||||
|
json const j1 = {{"one", 1}, {"two", 2}, {"three", 3}};
|
||||||
|
json const j2 = {{"one", 1u}, {"two", 2u}, {"three", 3u}};
|
||||||
|
json const j3 = {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}};
|
||||||
|
json const j4 = {{"one", true}, {"two", false}, {"three", true}};
|
||||||
|
json const j5 = {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}};
|
||||||
|
|
||||||
|
SECTION("std::map")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::map<std::string, int>>() == (std::map<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
||||||
|
CHECK(j2.get<std::map<std::string, unsigned int>>() == (std::map<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
||||||
|
CHECK(j3.get<std::map<std::string, double>>() == (std::map<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
||||||
|
CHECK(j4.get<std::map<std::string, bool>>() == (std::map<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
||||||
|
CHECK(j5.get<std::map<std::string, std::string>>() == (std::map<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_map")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::unordered_map<std::string, int>>() == (std::unordered_map<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
||||||
|
CHECK(j2.get<std::unordered_map<std::string, unsigned int>>() == (std::unordered_map<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
||||||
|
CHECK(j3.get<std::unordered_map<std::string, double>>() == (std::unordered_map<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
||||||
|
CHECK(j4.get<std::unordered_map<std::string, bool>>() == (std::unordered_map<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
||||||
|
const auto m5 = j5.get<std::unordered_map<std::string, std::string>>();
|
||||||
|
CHECK(m5 == (std::unordered_map<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
||||||
|
CHECK(m5.at("one") == "eins");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("reserve is called on containers that support it (#5406)")
|
||||||
|
{
|
||||||
|
// build a larger object so that a missing/incorrect reserve()
|
||||||
|
// call would be more likely to corrupt or drop elements
|
||||||
|
json j_large;
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
j_large[std::to_string(i)] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_map (supports reserve)")
|
||||||
|
{
|
||||||
|
const auto m = j_large.get<std::unordered_map<std::string, int>>();
|
||||||
|
CHECK(m.size() == 100);
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
CHECK(m.at(std::to_string(i)) == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::map (no reserve, fallback path)")
|
||||||
|
{
|
||||||
|
const auto m = j_large.get<std::map<std::string, int>>();
|
||||||
|
CHECK(m.size() == 100);
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
CHECK(m.at(std::to_string(i)) == i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::multimap")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::multimap<std::string, int>>() == (std::multimap<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
||||||
|
CHECK(j2.get<std::multimap<std::string, unsigned int>>() == (std::multimap<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
||||||
|
CHECK(j3.get<std::multimap<std::string, double>>() == (std::multimap<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
||||||
|
CHECK(j4.get<std::multimap<std::string, bool>>() == (std::multimap<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
||||||
|
const auto m5 = j5.get<std::multimap<std::string, std::string>>();
|
||||||
|
CHECK(m5 == (std::multimap<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
||||||
|
CHECK(m5.find("one")->second == "eins");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_multimap")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::unordered_multimap<std::string, int>>() == (std::unordered_multimap<std::string, int> {{"one", 1}, {"two", 2}, {"three", 3}}));
|
||||||
|
CHECK(j2.get<std::unordered_multimap<std::string, unsigned int>>() == (std::unordered_multimap<std::string, unsigned int> {{"one", 1u}, {"two", 2u}, {"three", 3u}}));
|
||||||
|
CHECK(j3.get<std::unordered_multimap<std::string, double>>() == (std::unordered_multimap<std::string, double> {{"one", 1.1}, {"two", 2.2}, {"three", 3.3}}));
|
||||||
|
CHECK(j4.get<std::unordered_multimap<std::string, bool>>() == (std::unordered_multimap<std::string, bool> {{"one", true}, {"two", false}, {"three", true}}));
|
||||||
|
const auto m5 = j5.get<std::unordered_multimap<std::string, std::string>>();
|
||||||
|
CHECK(m5 == (std::unordered_multimap<std::string, std::string> {{"one", "eins"}, {"two", "zwei"}, {"three", "drei"}}));
|
||||||
|
CHECK(m5.find("one")->second == "eins");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("exception in case of a non-object type")
|
||||||
|
{
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::map<std::string, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be object, but is null", json::type_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array-like STL containers")
|
||||||
|
{
|
||||||
|
json const j1 = {1, 2, 3, 4};
|
||||||
|
json const j2 = {1u, 2u, 3u, 4u};
|
||||||
|
json const j3 = {1.2, 2.3, 3.4, 4.5};
|
||||||
|
json const j4 = {true, false, true};
|
||||||
|
json const j5 = {"one", "two", "three"};
|
||||||
|
|
||||||
|
SECTION("std::list")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::list<int>>() == (std::list<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::list<unsigned int>>() == (std::list<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::list<double>>() == (std::list<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::list<bool>>() == (std::list<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::list<std::string>>() == (std::list<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::forward_list")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::forward_list<int>>() == (std::forward_list<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::forward_list<unsigned int>>() == (std::forward_list<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::forward_list<double>>() == (std::forward_list<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::forward_list<bool>>() == (std::forward_list<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::forward_list<std::string>>() == (std::forward_list<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::array")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::array<int, 4>>() == (std::array<int, 4> {{1, 2, 3, 4}}));
|
||||||
|
// only the first 3 elements of j2 are converted, since the target array is smaller
|
||||||
|
CHECK(j2.get<std::array<unsigned int, 3>>() == (std::array<unsigned int, 3> {{1u, 2u, 3u}}));
|
||||||
|
CHECK(j3.get<std::array<double, 4>>() == (std::array<double, 4> {{1.2, 2.3, 3.4, 4.5}}));
|
||||||
|
CHECK(j4.get<std::array<bool, 3>>() == (std::array<bool, 3> {{true, false, true}}));
|
||||||
|
CHECK(j5.get<std::array<std::string, 3>>() == (std::array<std::string, 3> {{"one", "two", "three"}}));
|
||||||
|
|
||||||
|
SECTION("std::array is larger than JSON")
|
||||||
|
{
|
||||||
|
std::array<int, 6> arr6 = {{1, 2, 3, 4, 5, 6}};
|
||||||
|
CHECK_THROWS_WITH_AS(j1.get_to(arr6), "[json.exception.out_of_range.401] "
|
||||||
|
"array index 4 is out of range", json::out_of_range&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::array is smaller than JSON")
|
||||||
|
{
|
||||||
|
std::array<int, 2> arr2 = {{8, 9}};
|
||||||
|
j1.get_to(arr2);
|
||||||
|
CHECK(arr2[0] == 1);
|
||||||
|
CHECK(arr2[1] == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::valarray")
|
||||||
|
{
|
||||||
|
// valarray has no operator== that returns bool, so compare via a vector copy
|
||||||
|
const auto v1 = j1.get<std::valarray<int>>();
|
||||||
|
CHECK((std::vector<int>(std::begin(v1), std::end(v1)) == std::vector<int> {1, 2, 3, 4}));
|
||||||
|
const auto v2 = j2.get<std::valarray<unsigned int>>();
|
||||||
|
CHECK((std::vector<unsigned int>(std::begin(v2), std::end(v2)) == std::vector<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
const auto v3 = j3.get<std::valarray<double>>();
|
||||||
|
CHECK((std::vector<double>(std::begin(v3), std::end(v3)) == std::vector<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
const auto v4 = j4.get<std::valarray<bool>>();
|
||||||
|
CHECK((std::vector<bool>(std::begin(v4), std::end(v4)) == std::vector<bool> {true, false, true}));
|
||||||
|
const auto v5 = j5.get<std::valarray<std::string>>();
|
||||||
|
CHECK((std::vector<std::string>(std::begin(v5), std::end(v5)) == std::vector<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::vector")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::vector<int>>() == (std::vector<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::vector<unsigned int>>() == (std::vector<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::vector<double>>() == (std::vector<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::vector<bool>>() == (std::vector<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::vector<std::string>>() == (std::vector<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::deque")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::deque<int>>() == (std::deque<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::deque<unsigned int>>() == (std::deque<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::deque<double>>() == (std::deque<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::deque<bool>>() == (std::deque<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::deque<std::string>>() == (std::deque<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::set")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::set<int>>() == (std::set<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::set<unsigned int>>() == (std::set<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::set<double>>() == (std::set<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::set<bool>>() == (std::set<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::set<std::string>>() == (std::set<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_set")
|
||||||
|
{
|
||||||
|
CHECK(j1.get<std::unordered_set<int>>() == (std::unordered_set<int> {1, 2, 3, 4}));
|
||||||
|
CHECK(j2.get<std::unordered_set<unsigned int>>() == (std::unordered_set<unsigned int> {1u, 2u, 3u, 4u}));
|
||||||
|
CHECK(j3.get<std::unordered_set<double>>() == (std::unordered_set<double> {1.2, 2.3, 3.4, 4.5}));
|
||||||
|
CHECK(j4.get<std::unordered_set<bool>>() == (std::unordered_set<bool> {true, false, true}));
|
||||||
|
CHECK(j5.get<std::unordered_set<std::string>>() == (std::unordered_set<std::string> {"one", "two", "three"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::map (array of pairs)")
|
||||||
|
{
|
||||||
|
const std::map<int, int> m{{0, 1}, {1, 2}, {2, 3}};
|
||||||
|
json const j6 = m;
|
||||||
|
|
||||||
|
auto m2 = j6.get<std::map<int, int>>();
|
||||||
|
CHECK(m == m2);
|
||||||
|
|
||||||
|
json const j7 = {0, 1, 2, 3};
|
||||||
|
json const j8 = 2;
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS((j7.get<std::map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] (/0) type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS((j7.get<std::map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
#endif
|
||||||
|
CHECK_THROWS_WITH_AS((j8.get<std::map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
|
||||||
|
SECTION("superfluous entries")
|
||||||
|
{
|
||||||
|
json const j9 = {{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
|
||||||
|
m2 = j9.get<std::map<int, int>>();
|
||||||
|
CHECK(m == m2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("std::unordered_map (array of pairs)")
|
||||||
|
{
|
||||||
|
const std::unordered_map<int, int> m{{0, 1}, {1, 2}, {2, 3}};
|
||||||
|
json const j6 = m;
|
||||||
|
|
||||||
|
auto m2 = j6.get<std::unordered_map<int, int>>();
|
||||||
|
CHECK(m == m2);
|
||||||
|
|
||||||
|
json const j7 = {0, 1, 2, 3};
|
||||||
|
json const j8 = 2;
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS((j7.get<std::unordered_map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] (/0) type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS((j7.get<std::unordered_map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
#endif
|
||||||
|
CHECK_THROWS_WITH_AS((j8.get<std::unordered_map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, "
|
||||||
|
"but is number", json::type_error&);
|
||||||
|
|
||||||
|
SECTION("superfluous entries")
|
||||||
|
{
|
||||||
|
json const j9{{0, 1, 2}, {1, 2, 3}, {2, 3, 4}};
|
||||||
|
m2 = j9.get<std::unordered_map<int, int>>();
|
||||||
|
CHECK(m == m2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("exception in case of a non-object type")
|
||||||
|
{
|
||||||
|
// does type really must be an array? or it rather must not be null?
|
||||||
|
// that's what I thought when other test like this one broke
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::list<int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::vector<int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::vector<json>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::list<json>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::valarray<int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(
|
||||||
|
(json().get<std::map<int, int>>()),
|
||||||
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class cards {kreuz, pik, herz, karo};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(cards,
|
||||||
|
{
|
||||||
|
{cards::kreuz, "kreuz"},
|
||||||
|
{cards::pik, "pik"},
|
||||||
|
{cards::pik, "puk"}, // second entry for cards::puk; will not be used
|
||||||
|
{cards::herz, "herz"},
|
||||||
|
{cards::karo, "karo"}
|
||||||
|
})
|
||||||
|
|
||||||
|
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
||||||
|
{
|
||||||
|
TS_STOPPED,
|
||||||
|
TS_RUNNING,
|
||||||
|
TS_COMPLETED,
|
||||||
|
TS_INVALID = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
|
||||||
|
{
|
||||||
|
{TS_INVALID, nullptr},
|
||||||
|
{TS_STOPPED, "stopped"},
|
||||||
|
{TS_RUNNING, "running"},
|
||||||
|
{TS_COMPLETED, "completed"},
|
||||||
|
})
|
||||||
|
|
||||||
|
TEST_CASE("JSON to enum mapping")
|
||||||
|
{
|
||||||
|
SECTION("enum class")
|
||||||
|
{
|
||||||
|
// enum -> json
|
||||||
|
CHECK(json(cards::kreuz) == "kreuz");
|
||||||
|
CHECK(json(cards::pik) == "pik");
|
||||||
|
CHECK(json(cards::herz) == "herz");
|
||||||
|
CHECK(json(cards::karo) == "karo");
|
||||||
|
|
||||||
|
// json -> enum
|
||||||
|
CHECK(cards::kreuz == json("kreuz"));
|
||||||
|
CHECK(cards::pik == json("pik"));
|
||||||
|
CHECK(cards::herz == json("herz"));
|
||||||
|
CHECK(cards::karo == json("karo"));
|
||||||
|
|
||||||
|
// invalid json -> first enum
|
||||||
|
CHECK(cards::kreuz == json("what?").get<cards>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("traditional enum")
|
||||||
|
{
|
||||||
|
// enum -> json
|
||||||
|
CHECK(json(TS_STOPPED) == "stopped");
|
||||||
|
CHECK(json(TS_RUNNING) == "running");
|
||||||
|
CHECK(json(TS_COMPLETED) == "completed");
|
||||||
|
CHECK(json(TS_INVALID) == json());
|
||||||
|
|
||||||
|
// json -> enum
|
||||||
|
CHECK(TS_STOPPED == json("stopped"));
|
||||||
|
CHECK(TS_RUNNING == json("running"));
|
||||||
|
CHECK(TS_COMPLETED == json("completed"));
|
||||||
|
CHECK(TS_INVALID == json());
|
||||||
|
|
||||||
|
// invalid json -> first enum
|
||||||
|
CHECK(TS_INVALID == json("what?").get<TaskState>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards,
|
||||||
|
{
|
||||||
|
{strict_cards::kreuz, "kreuz"},
|
||||||
|
{strict_cards::pik, "pik"},
|
||||||
|
{strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used
|
||||||
|
{strict_cards::herz, "herz"},
|
||||||
|
{strict_cards::karo, "karo"}
|
||||||
|
})
|
||||||
|
|
||||||
|
enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
||||||
|
{
|
||||||
|
STRICT_TS_STOPPED,
|
||||||
|
STRICT_TS_RUNNING,
|
||||||
|
STRICT_TS_COMPLETED,
|
||||||
|
STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping
|
||||||
|
STRICT_TS_INVALID = -1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState,
|
||||||
|
{
|
||||||
|
{STRICT_TS_INVALID, nullptr},
|
||||||
|
{STRICT_TS_STOPPED, "stopped"},
|
||||||
|
{STRICT_TS_RUNNING, "running"},
|
||||||
|
{STRICT_TS_COMPLETED, "completed"},
|
||||||
|
})
|
||||||
|
|
||||||
|
// regression test for #5708 item 2: NLOHMANN_JSON_SERIALIZE_ENUM_STRICT must not rely on
|
||||||
|
// unqualified lookup of a helper name that a user's own namespace may also declare
|
||||||
|
namespace ns_with_colliding_name
|
||||||
|
{
|
||||||
|
// NOLINTNEXTLINE(misc-use-internal-linkage) - used to shadow the library's internal helper name
|
||||||
|
inline void templated_json_throw(int /*unused*/) {}
|
||||||
|
|
||||||
|
enum class colliding_enum { a, b };
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
||||||
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(colliding_enum,
|
||||||
|
{
|
||||||
|
{colliding_enum::a, "a"},
|
||||||
|
{colliding_enum::b, "b"}
|
||||||
|
})
|
||||||
|
} // namespace ns_with_colliding_name
|
||||||
|
|
||||||
|
TEST_CASE("NLOHMANN_JSON_SERIALIZE_ENUM_STRICT in a namespace with a colliding name")
|
||||||
|
{
|
||||||
|
using ns_with_colliding_name::colliding_enum;
|
||||||
|
|
||||||
|
CHECK(json(colliding_enum::a) == "a");
|
||||||
|
CHECK(colliding_enum::b == json("b"));
|
||||||
|
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json("nope").get<colliding_enum>(), "[json.exception.out_of_range.410] enum value out of range for colliding_enum: \"nope\"", json::out_of_range&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Strict JSON to enum mapping")
|
||||||
|
{
|
||||||
|
SECTION("enum class")
|
||||||
|
{
|
||||||
|
// enum -> json
|
||||||
|
CHECK(json(strict_cards::kreuz) == "kreuz");
|
||||||
|
CHECK(json(strict_cards::pik) == "pik");
|
||||||
|
CHECK(json(strict_cards::herz) == "herz");
|
||||||
|
CHECK(json(strict_cards::karo) == "karo");
|
||||||
|
|
||||||
|
// json -> enum
|
||||||
|
CHECK(json("kreuz").get<strict_cards>() == strict_cards::kreuz);
|
||||||
|
CHECK(json("pik").get<strict_cards>() == strict_cards::pik);
|
||||||
|
CHECK(json("herz").get<strict_cards>() == strict_cards::herz);
|
||||||
|
CHECK(json("karo").get<strict_cards>() == strict_cards::karo);
|
||||||
|
|
||||||
|
// comparison of enum and json
|
||||||
|
CHECK(strict_cards::kreuz == json("kreuz"));
|
||||||
|
CHECK(strict_cards::pik == json("pik"));
|
||||||
|
CHECK(strict_cards::herz == json("herz"));
|
||||||
|
CHECK(strict_cards::karo == json("karo"));
|
||||||
|
|
||||||
|
// invalid json -> exception thrown
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json("what?").get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"what?\"", json::out_of_range&);
|
||||||
|
|
||||||
|
// conversion of unmapped enum -> exception thrown
|
||||||
|
CHECK_THROWS_WITH_AS(json(strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
||||||
|
|
||||||
|
// comparing an unmapped enum with json throws the same exception
|
||||||
|
// (the scalar comparison operators used to be noexcept, so this
|
||||||
|
// called std::terminate)
|
||||||
|
CHECK_THROWS_WITH_AS(static_cast<void>(strict_cards::andere == json("andere")), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
||||||
|
CHECK_THROWS_WITH_AS(static_cast<void>(json("andere") != strict_cards::andere), "[json.exception.out_of_range.410] enum value out of range for strict_cards", json::out_of_range&);
|
||||||
|
|
||||||
|
// invalid UTF-8 -> out_of_range.410, not the type_error.316 thrown while building the
|
||||||
|
// message (regression test for #5667); such strings can reach get<Enum>() unvalidated,
|
||||||
|
// e.g. from from_cbor()/from_msgpack() (#5529)
|
||||||
|
const json j_invalid_utf8 = "\xFF";
|
||||||
|
CHECK_THROWS_WITH_AS(_ = j_invalid_utf8.get<strict_cards>(), "[json.exception.out_of_range.410] enum value out of range for strict_cards: \"\xEF\xBF\xBD\"", json::out_of_range&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("traditional enum")
|
||||||
|
{
|
||||||
|
// enum -> json
|
||||||
|
CHECK(json(STRICT_TS_STOPPED) == "stopped");
|
||||||
|
CHECK(json(STRICT_TS_RUNNING) == "running");
|
||||||
|
CHECK(json(STRICT_TS_COMPLETED) == "completed");
|
||||||
|
CHECK(json(STRICT_TS_INVALID) == json());
|
||||||
|
|
||||||
|
// json -> enum
|
||||||
|
CHECK(json("stopped").get<StrictTaskState>() == STRICT_TS_STOPPED);
|
||||||
|
CHECK(json("running").get<StrictTaskState>() == STRICT_TS_RUNNING);
|
||||||
|
CHECK(json("completed").get<StrictTaskState>() == STRICT_TS_COMPLETED);
|
||||||
|
CHECK(json().get<StrictTaskState>() == STRICT_TS_INVALID);
|
||||||
|
|
||||||
|
// comparison of enum and json
|
||||||
|
CHECK(STRICT_TS_STOPPED == json("stopped"));
|
||||||
|
CHECK(STRICT_TS_RUNNING == json("running"));
|
||||||
|
CHECK(STRICT_TS_COMPLETED == json("completed"));
|
||||||
|
CHECK(STRICT_TS_INVALID == json());
|
||||||
|
|
||||||
|
// invalid json -> exception thrown
|
||||||
|
json _;
|
||||||
|
CHECK_THROWS_WITH_AS(_ = json("what?").get<StrictTaskState>(), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState: \"what?\"", json::out_of_range&);
|
||||||
|
|
||||||
|
// conversion of unmapped enum -> exception thrown
|
||||||
|
CHECK_THROWS_WITH_AS(json(STRICT_TS_OTHER), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
|
||||||
|
|
||||||
|
// comparing an unmapped enum with json throws the same exception
|
||||||
|
CHECK_THROWS_WITH_AS(static_cast<void>(STRICT_TS_OTHER < json("x")), "[json.exception.out_of_range.410] enum value out of range for StrictTaskState", json::out_of_range&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||||
|
TEST_CASE("std::filesystem::path")
|
||||||
|
{
|
||||||
|
SECTION("ascii")
|
||||||
|
{
|
||||||
|
json const j_string = "Path";
|
||||||
|
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
|
||||||
|
json const j_path = p;
|
||||||
|
|
||||||
|
CHECK(j_path.template get<std::string>() ==
|
||||||
|
j_string.template get<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("utf-8")
|
||||||
|
{
|
||||||
|
json const j_string = "P\xc4\x9b\xc5\xa1ina";
|
||||||
|
auto p = j_string.template get<nlohmann::detail::std_fs::path>();
|
||||||
|
json const j_path = p;
|
||||||
|
|
||||||
|
CHECK(j_path.template get<std::string>() ==
|
||||||
|
j_string.template get<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// the ADL to_json overload for std::u8string only exists under the same guard
|
||||||
|
// as std::filesystem::path support (it is otherwise only reached indirectly,
|
||||||
|
// via std::filesystem::path::u8string()) -- mirror both #if conditions from
|
||||||
|
// include/nlohmann/detail/conversions/to_json.hpp exactly
|
||||||
|
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
||||||
|
#if defined(__cpp_lib_char8_t)
|
||||||
|
TEST_CASE("std::u8string")
|
||||||
|
{
|
||||||
|
SECTION("ascii")
|
||||||
|
{
|
||||||
|
const std::u8string s = u8"Path";
|
||||||
|
json const j = s;
|
||||||
|
|
||||||
|
CHECK(j.template get<std::string>() == "Path");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("utf-8")
|
||||||
|
{
|
||||||
|
// use \u universal-character-names (rather than raw \x byte escapes
|
||||||
|
// or literal non-ASCII source bytes) to compose the multi-byte UTF-8
|
||||||
|
// encoding -- MSVC treats \x escapes used that way inside a u8
|
||||||
|
// literal as a nonstandard extension (warning C5321), which some of
|
||||||
|
// our CI configs promote to an error; \u is portable and produces
|
||||||
|
// the exact same encoded bytes without depending on the source
|
||||||
|
// file's encoding
|
||||||
|
const std::u8string s = u8"P\u011B\u0161ina";
|
||||||
|
json const j = s;
|
||||||
|
|
||||||
|
CHECK(j.template get<std::string>() == "P\xc4\x9b\xc5\xa1ina");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// a type whose to_json reports an error by throwing, used below to check that
|
||||||
|
// converting a std::optional<T> to JSON propagates an exception thrown while
|
||||||
|
// converting its contained value instead of calling std::terminate (#5642)
|
||||||
|
struct throwing_to_json_type {};
|
||||||
|
|
||||||
|
[[noreturn]] void to_json(json& /*unused*/, const throwing_to_json_type& /*unused*/)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("cannot serialize throwing_to_json_type");
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("std::optional")
|
||||||
|
{
|
||||||
|
SECTION("null")
|
||||||
|
{
|
||||||
|
const json j_null;
|
||||||
|
const std::optional<std::string> opt_null;
|
||||||
|
|
||||||
|
CHECK(json(opt_null) == j_null);
|
||||||
|
CHECK(j_null.get<std::optional<std::string>>() == std::nullopt);
|
||||||
|
|
||||||
|
// Constructing std::optional<T> directly from JSON null throws because
|
||||||
|
// std::optional's own converting constructor is chosen over basic_json's
|
||||||
|
// operator T(). This is a language-level limitation (std::optional<T> is
|
||||||
|
// constructible from T, and T is constructible from basic_json via the
|
||||||
|
// operator); there is no SFINAE path that distinguishes "call from inside
|
||||||
|
// std::optional's constructor" from "direct call". Use get<std::optional<T>>()
|
||||||
|
// or get_to() instead for correct null handling. See #4864 and #5246.
|
||||||
|
CHECK_THROWS_WITH_AS(std::optional<std::string>(j_null),
|
||||||
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||||
|
CHECK_THROWS_WITH_AS(std::optional<int>(j_null),
|
||||||
|
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
||||||
|
|
||||||
|
// Assignment goes through the same overload resolution as direct
|
||||||
|
// construction, so it throws for the same reason. This relies on
|
||||||
|
// basic_json's implicit conversion operator, so it only applies
|
||||||
|
// when JSON_USE_IMPLICIT_CONVERSIONS is enabled (the default).
|
||||||
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
||||||
|
std::optional<std::string> opt_assign;
|
||||||
|
CHECK_THROWS_WITH_AS(opt_assign = j_null,
|
||||||
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// get_to() is the correct way to obtain std::nullopt from a JSON null.
|
||||||
|
std::optional<std::string> opt_get_to = "placeholder";
|
||||||
|
j_null.get_to(opt_get_to);
|
||||||
|
CHECK(opt_get_to == std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("string")
|
||||||
|
{
|
||||||
|
json j_string = "string";
|
||||||
|
std::optional<std::string> opt_string = "string";
|
||||||
|
|
||||||
|
CHECK(json(opt_string) == j_string);
|
||||||
|
CHECK(std::optional<std::string>(j_string) == opt_string);
|
||||||
|
// false positive: Infer attributes the destruction of the temporaries above to opt_string
|
||||||
|
// @infer-ignore USE_AFTER_DELETE
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("bool")
|
||||||
|
{
|
||||||
|
json j_bool = true;
|
||||||
|
std::optional<bool> opt_bool = true;
|
||||||
|
|
||||||
|
CHECK(json(opt_bool) == j_bool);
|
||||||
|
CHECK(std::optional<bool>(j_bool) == opt_bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("number")
|
||||||
|
{
|
||||||
|
json j_number = 1;
|
||||||
|
std::optional<int> opt_int = 1;
|
||||||
|
|
||||||
|
CHECK(json(opt_int) == j_number);
|
||||||
|
CHECK(j_number.get<std::optional<int>>() == opt_int);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("array")
|
||||||
|
{
|
||||||
|
json j_array = {1, 2, nullptr};
|
||||||
|
std::vector<std::optional<int>> opt_array = {{1, 2, std::nullopt}};
|
||||||
|
|
||||||
|
CHECK(json(opt_array) == j_array);
|
||||||
|
CHECK(j_array.get<std::vector<std::optional<int>>>() == opt_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("object")
|
||||||
|
{
|
||||||
|
json j_object = {{"one", 1}, {"two", 2}, {"zero", nullptr}};
|
||||||
|
std::map<std::string, std::optional<int>> opt_object {{"one", 1}, {"two", 2}, {"zero", std::nullopt}};
|
||||||
|
|
||||||
|
CHECK(json(opt_object) == j_object);
|
||||||
|
CHECK(std::map<std::string, std::optional<int>>(j_object) == opt_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(JSON_NOEXCEPTION)
|
||||||
|
SECTION("exception from contained value's to_json propagates (#5642)")
|
||||||
|
{
|
||||||
|
// to_json(BasicJsonType&, const std::optional<T>&) must not be
|
||||||
|
// noexcept: it calls T's to_json, which may throw (a user-defined
|
||||||
|
// to_json that reports an error, or std::bad_alloc for T =
|
||||||
|
// std::string/vector/json). Before the fix, this called
|
||||||
|
// std::terminate() instead of letting the exception propagate.
|
||||||
|
const std::optional<throwing_to_json_type> opt = throwing_to_json_type{};
|
||||||
|
CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&);
|
||||||
|
|
||||||
|
// the conversion is noexcept exactly when converting the contained value is
|
||||||
|
static_assert(!std::is_nothrow_constructible<json, const std::optional<throwing_to_json_type>&>::value);
|
||||||
|
static_assert(std::is_nothrow_constructible<json, const std::optional<int>&>::value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
#undef JSON_HAS_CPP_17
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef JSON_HAS_CPP_14
|
||||||
|
#undef JSON_HAS_CPP_14
|
||||||
|
#endif
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||||
@@ -505,7 +505,7 @@ static json_with_const_base make_nested_array(std::size_t depth)
|
|||||||
{
|
{
|
||||||
if (depth == 0)
|
if (depth == 0)
|
||||||
{
|
{
|
||||||
return json_with_const_base(1);
|
return json_with_const_base(1); // NOLINT(modernize-return-braced-init-list): {1} would be an array
|
||||||
}
|
}
|
||||||
return json_with_const_base::array({make_nested_array(depth - 1)});
|
return json_with_const_base::array({make_nested_array(depth - 1)});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ DOCTEST_GCC_SUPPRESS_WARNING("-Wnoexcept")
|
|||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
||||||
|
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
||||||
|
#endif
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
// for #2824
|
// for #2824
|
||||||
|
|||||||
@@ -1963,8 +1963,8 @@ TEST_CASE("operator[] with user-defined std::string_view-convertible types")
|
|||||||
};
|
};
|
||||||
|
|
||||||
json j = {{"foo", "from_class"}, {"bar", "from_struct"}};
|
json j = {{"foo", "from_class"}, {"bar", "from_struct"}};
|
||||||
TestClass foo_obj;
|
const TestClass foo_obj;
|
||||||
TestStruct bar_obj;
|
const TestStruct bar_obj;
|
||||||
|
|
||||||
SECTION("read access")
|
SECTION("read access")
|
||||||
{
|
{
|
||||||
@@ -2005,6 +2005,10 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions (
|
|||||||
// 3.12.0, such a key worked with at, the const operator[], find, count and
|
// 3.12.0, such a key worked with at, the const operator[], find, count and
|
||||||
// contains via the conversion to std::string; #4958 made the KeyType&&
|
// contains via the conversion to std::string; #4958 made the KeyType&&
|
||||||
// templates win overload resolution for it instead, and those then failed
|
// templates win overload resolution for it instead, and those then failed
|
||||||
|
// the lookups pick the conversion to std::string_view, which leaves the one
|
||||||
|
// to std::string unused; it has to exist to reproduce the ambiguity
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING("-Wunused-member-function")
|
||||||
struct DualKey
|
struct DualKey
|
||||||
{
|
{
|
||||||
operator std::string() const
|
operator std::string() const
|
||||||
@@ -2016,6 +2020,7 @@ TEST_CASE("keys convertible to std::string_view work with all lookup functions (
|
|||||||
return "a";
|
return "a";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
DOCTEST_CLANG_SUPPRESS_WARNING_POP
|
||||||
|
|
||||||
SECTION("nlohmann::json")
|
SECTION("nlohmann::json")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1162,6 +1162,7 @@ TEST_CASE("update() on deeply nested values")
|
|||||||
|
|
||||||
TEST_CASE("update() with an argument that aliases *this (#5641)")
|
TEST_CASE("update() with an argument that aliases *this (#5641)")
|
||||||
{
|
{
|
||||||
|
#if !defined(JSON_NOEXCEPTION) // checks which exception is thrown, and that nothing changed
|
||||||
SECTION("the target is checked before the argument, as before the copy")
|
SECTION("the target is checked before the argument, as before the copy")
|
||||||
{
|
{
|
||||||
json j = 1;
|
json j = 1;
|
||||||
@@ -1172,6 +1173,7 @@ TEST_CASE("update() with an argument that aliases *this (#5641)")
|
|||||||
CHECK_THROWS_WITH_AS(k.update(json::array()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
CHECK_THROWS_WITH_AS(k.update(json::array()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&);
|
||||||
CHECK(k == json::object());
|
CHECK(k == json::object());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
SECTION("const reference")
|
SECTION("const reference")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1560,7 +1560,7 @@ TEST_CASE("MessagePack")
|
|||||||
// dump() still requires valid UTF-8 and throws for such a value,
|
// dump() still requires valid UTF-8 and throws for such a value,
|
||||||
// unless an error handler that replaces or ignores the bytes is
|
// unless an error handler that replaces or ignores the bytes is
|
||||||
// passed
|
// passed
|
||||||
CHECK_THROWS_AS(j_value.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(j_value.dump()), json::type_error&);
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
// the same bytes as an object key round-trip as well
|
||||||
const std::vector<uint8_t> ill_formed_key = {0x81, 0xa2, 0xc0, 0xae, 0x01};
|
const std::vector<uint8_t> ill_formed_key = {0x81, 0xa2, 0xc0, 0xae, 0x01};
|
||||||
|
|||||||
@@ -430,8 +430,8 @@ TEST_CASE("ordered_map")
|
|||||||
|
|
||||||
SECTION("with T& (lvalue)")
|
SECTION("with T& (lvalue)")
|
||||||
{
|
{
|
||||||
std::string one = "1";
|
std::string one = "1"; // NOLINT(misc-const-correctness): emplace must accept a non-const lvalue
|
||||||
std::string four = "four";
|
std::string four = "four"; // NOLINT(misc-const-correctness): see above
|
||||||
|
|
||||||
auto res1 = om.emplace("eins", one);
|
auto res1 = om.emplace("eins", one);
|
||||||
CHECK(res1.first == om.begin());
|
CHECK(res1.first == om.begin());
|
||||||
@@ -467,7 +467,7 @@ TEST_CASE("ordered_map")
|
|||||||
SECTION("with key of key_type (non-template overload)")
|
SECTION("with key of key_type (non-template overload)")
|
||||||
{
|
{
|
||||||
const std::string key_vier{"vier"};
|
const std::string key_vier{"vier"};
|
||||||
std::string four = "four";
|
std::string four = "four"; // NOLINT(misc-const-correctness): emplace must accept a non-const lvalue
|
||||||
|
|
||||||
auto res4 = om.emplace(key_vier, four);
|
auto res4 = om.emplace(key_vier, four);
|
||||||
CHECK(res4.first == om.begin() + 3);
|
CHECK(res4.first == om.begin() + 3);
|
||||||
|
|||||||
@@ -2520,7 +2520,7 @@ TEST_CASE("Universal Binary JSON Specification Examples 1")
|
|||||||
CHECK_NOTHROW(j = json::from_ubjson(v));
|
CHECK_NOTHROW(j = json::from_ubjson(v));
|
||||||
REQUIRE(j.is_string());
|
REQUIRE(j.is_string());
|
||||||
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
CHECK(j.get_ref<const json::string_t&>() == std::string("\xc0\xae"));
|
||||||
CHECK_THROWS_AS(j.dump(), json::type_error&);
|
CHECK_THROWS_AS(utils::ignore_return_value(j.dump()), json::type_error&);
|
||||||
CHECK(json::from_ubjson(json::to_ubjson(j)) == j);
|
CHECK(json::from_ubjson(json::to_ubjson(j)) == j);
|
||||||
|
|
||||||
// the same bytes as an object key round-trip as well
|
// the same bytes as an object key round-trip as well
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ TEST_CASE("wide strings")
|
|||||||
// 32-bit wchar_t first encodes it as an ill-formed three-byte
|
// 32-bit wchar_t first encodes it as an ill-formed three-byte
|
||||||
// sequence (rejected one byte later, at column 3)
|
// sequence (rejected one byte later, at column 3)
|
||||||
const char* const error_low_surrogate = sizeof(wchar_t) == 2
|
const char* const error_low_surrogate = sizeof(wchar_t) == 2
|
||||||
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xFF'"
|
||||||
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'";
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xB0'";
|
||||||
const char* const error_high_surrogate = sizeof(wchar_t) == 2
|
const char* const error_high_surrogate = sizeof(wchar_t) == 2
|
||||||
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"<U+0000>'"
|
? "[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xFF'"
|
||||||
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'";
|
: "[json.exception.parse_error.101] parse error at line 1, column 3: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"\xED\xA0'";
|
||||||
|
|
||||||
// a lone low surrogate cannot start a pair
|
// a lone low surrogate cannot start a pair
|
||||||
|
|||||||
Reference in New Issue
Block a user