From ba239535a3234960eb8cb337ca9197f84499cad3 Mon Sep 17 00:00:00 2001 From: domsoll <76645975+domsoll@users.noreply.github.com> Date: Tue, 29 Dec 2020 18:42:19 +0100 Subject: [PATCH 1/5] CHG: explicit size checks and more expressive exceptions for converstion of fixed size containers from json MNT: updated amalgamation header --- .../nlohmann/detail/conversions/from_json.hpp | 32 +++++++++++++++++ single_include/nlohmann/json.hpp | 36 +++++++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 438b84a2e..5695fbed3 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -164,6 +164,14 @@ template auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get(), void()) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != N)) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); + } for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -181,6 +189,14 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != N)) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); + } for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -326,12 +342,28 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) template void from_json(const BasicJsonType& j, std::pair& p) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != 2)) + { + JSON_THROW(type_error::create(302, "array size must be 2, but is " + std::to_string(j.size()))); + } p = {j.at(0).template get(), j.at(1).template get()}; } template void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != sizeof...(Idx))) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(sizeof...(Idx)) + ", but is " + std::to_string(j.size()))); + } t = std::make_tuple(j.at(Idx).template get::type>()...); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index a70aaf8cb..e374a3e6b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3564,6 +3564,14 @@ template auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get(), void()) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != N)) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); + } for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -3581,6 +3589,14 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != N)) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); + } for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -3726,12 +3742,28 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) template void from_json(const BasicJsonType& j, std::pair& p) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != 2)) + { + JSON_THROW(type_error::create(302, "array size must be 2, but is " + std::to_string(j.size()))); + } p = {j.at(0).template get(), j.at(1).template get()}; } template void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) { + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); + } + if (JSON_HEDLEY_UNLIKELY(j.size() != sizeof...(Idx))) + { + JSON_THROW(type_error::create(302, "array size must be " + std::to_string(sizeof...(Idx)) + ", but is " + std::to_string(j.size()))); + } t = std::make_tuple(j.at(Idx).template get::type>()...); } @@ -16686,7 +16718,7 @@ class basic_json detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments); @@ -25227,7 +25259,7 @@ template<> inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( is_nothrow_move_constructible::value&& is_nothrow_move_assignable::value - ) +) { j1.swap(j2); } From ca29a1e5e426904d9946fb646f0bfb2dcca5d7cc Mon Sep 17 00:00:00 2001 From: domsoll <76645975+domsoll@users.noreply.github.com> Date: Tue, 29 Dec 2020 19:22:43 +0100 Subject: [PATCH 2/5] CHG: expect strict size check in std::array test case ENH: test cases for std::pair and std::tuple added --- test/src/unit-conversions.cpp | 101 +++++++++++++++++++++++++++++++--- 1 file changed, 94 insertions(+), 7 deletions(-) diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index de8040c87..eb20e832f 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -41,6 +41,8 @@ using nlohmann::json; #include #include #include +#include +#include #if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 #define JSON_HAS_CPP_17 @@ -1488,7 +1490,7 @@ TEST_CASE("value conversion") SECTION("std::array") { j1.get>(); - j2.get>(); + j2.get>(); j3.get>(); j4.get>(); j5.get>(); @@ -1496,17 +1498,17 @@ TEST_CASE("value conversion") SECTION("std::array is larger than JSON") { std::array arr6 = {{1, 2, 3, 4, 5, 6}}; - CHECK_THROWS_AS(j1.get_to(arr6), json::out_of_range&); - CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.out_of_range.401] " - "array index 4 is out of range"); + CHECK_THROWS_AS(j1.get_to(arr6), json::type_error&); + CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.type_error.302] " + "array size must be 6, but is 4"); } SECTION("std::array is smaller than JSON") { std::array arr2 = {{8, 9}}; - j1.get_to(arr2); - CHECK(arr2[0] == 1); - CHECK(arr2[1] == 2); + CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); + CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " + "array size must be 2, but is 4"); } } @@ -1639,6 +1641,91 @@ TEST_CASE("value conversion") "[json.exception.type_error.302] type must be array, but is null"); } } + + SECTION("other STL containers") + { + json j1 = {1, 2}; + json j2 = {1u, 2u}; + json j3 = {1.2, 2.3}; + json j4 = {true, false}; + json j5 = {"one", "two"}; + json j6 = {1, "2"}; + json j7 = {1, 2.3}; + json j8 = {1, false}; + json j9 = {1, 2, 3}; + json j10 = {1}; + + SECTION("std::pair") + { + j1.get>(); + j2.get>(); + j3.get>(); + j4.get>(); + j5.get>(); + j6.get>(); + j7.get>(); + j8.get>(); + + SECTION("std::pair is larger than JSON") + { + std::pair p; + CHECK_THROWS_AS(j10.get_to(p), json::type_error&); + CHECK_THROWS_WITH(j10.get_to(p), "[json.exception.type_error.302] " + "array size must be 2, but is 1"); + } + + SECTION("std::pair is smaller than JSON") + { + std::pair p; + CHECK_THROWS_AS(j9.get_to(p), json::type_error&); + CHECK_THROWS_WITH(j9.get_to(p), "[json.exception.type_error.302] " + "array size must be 2, but is 3"); + } + } + + SECTION("std::tuple") + { + j1.get>(); + j2.get>(); + j3.get>(); + j4.get>(); + j5.get>(); + j6.get>(); + j7.get>(); + j8.get>(); + j9.get>(); + j10.get>(); + + SECTION("std::tuple is larger than JSON") + { + std::tuple t; + CHECK_THROWS_AS(j10.get_to(t), json::type_error&); + CHECK_THROWS_WITH(j10.get_to(t), "[json.exception.type_error.302] " + "array size must be 2, but is 1"); + } + + SECTION("std::tuple is smaller than JSON") + { + std::tuple t; + CHECK_THROWS_AS(j9.get_to(t), json::type_error&); + CHECK_THROWS_WITH(j9.get_to(t), "[json.exception.type_error.302] " + "array size must be 2, but is 3"); + } + } + + SECTION("exception in case of a non-object type") + { + CHECK_THROWS_AS((json().get>()), json::type_error&); + CHECK_THROWS_AS((json().get>()), json::type_error&); + + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null"); + } + } } } From 748f4ec2279ab7acebf17d38e4f42e3013c68021 Mon Sep 17 00:00:00 2001 From: domsoll <76645975+domsoll@users.noreply.github.com> Date: Tue, 29 Dec 2020 21:22:49 +0100 Subject: [PATCH 3/5] CHG: expect strict size check in std::array, pair and tuple test cases ENH: more test cases for c arrays and std::array added MNT: redundant array type check for std::array conversion reverted MNT: updated amalgamation header --- .../nlohmann/detail/conversions/from_json.hpp | 5 +-- single_include/nlohmann/json.hpp | 5 +-- test/src/unit-constructor1.cpp | 40 +++++++------------ test/src/unit-conversions.cpp | 35 ++++++++++++++++ 4 files changed, 51 insertions(+), 34 deletions(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index 5695fbed3..b85a4e3d6 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -189,10 +189,7 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } + // array type check done in from_json already, only check size if (JSON_HEDLEY_UNLIKELY(j.size() != N)) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e374a3e6b..8db2a0eff 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3589,10 +3589,7 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { - if (JSON_HEDLEY_UNLIKELY(!j.is_array())) - { - JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); - } + // array type check done in from_json already, only check size if (JSON_HEDLEY_UNLIKELY(j.size() != N)) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index 33bd92c3b..b916a313c 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -269,15 +269,6 @@ TEST_CASE("constructors") CHECK(j[1] == std::get<1>(p)); } - SECTION("std::pair with discarded values") - { - json j{1, 2.0, "string"}; - - const auto p = j.get>(); - CHECK(p.first == j[0]); - CHECK(p.second == j[1]); - } - SECTION("std::tuple") { const auto t = std::make_tuple(1.0, std::string{"string"}, 42, std::vector {0, 1}); @@ -293,26 +284,23 @@ TEST_CASE("constructors") CHECK(j[3][1] == 1); } - SECTION("std::tuple with discarded values") - { - json j{1, 2.0, "string", 42}; - - const auto t = j.get>(); - CHECK(std::get<0>(t) == j[0]); - CHECK(std::get<1>(t) == j[1]); - CHECK(std::get<2>(t) == j[2]); - } - SECTION("std::pair/tuple/array failures") { - json j{1}; + json j1{1}; + json j2{1, 2, 3}; - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); - CHECK_THROWS_AS((j.get>()), json::out_of_range&); - CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); + CHECK_THROWS_AS((j1.get>()), json::type_error&); + CHECK_THROWS_WITH((j1.get>()), "[json.exception.type_error.302] array size must be 2, but is 1"); + CHECK_THROWS_AS((j2.get>()), json::type_error&); + CHECK_THROWS_WITH((j2.get>()), "[json.exception.type_error.302] array size must be 2, but is 3"); + CHECK_THROWS_AS((j1.get>()), json::type_error&); + CHECK_THROWS_WITH((j1.get>()), "[json.exception.type_error.302] array size must be 2, but is 1"); + CHECK_THROWS_AS((j2.get>()), json::type_error&); + CHECK_THROWS_WITH((j2.get>()), "[json.exception.type_error.302] array size must be 2, but is 3"); + CHECK_THROWS_AS((j1.get>()), json::type_error&); + CHECK_THROWS_WITH((j1.get>()), "[json.exception.type_error.302] array size must be 3, but is 1"); + CHECK_THROWS_AS((j2.get>()), json::type_error&); + CHECK_THROWS_WITH((j2.get>()), "[json.exception.type_error.302] array size must be 1, but is 3"); } SECTION("std::forward_list") diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index eb20e832f..48b410fe7 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -400,6 +400,33 @@ TEST_CASE("value conversion") json j2 = nbs; j2.get_to(nbs2); CHECK(std::equal(std::begin(nbs), std::end(nbs), std::begin(nbs2))); + + SECTION("built-in array is larger than JSON") + { + json j1 = {1, 2, 3, 4}; + int arr6[] = {1, 2, 3, 4, 5, 6}; + CHECK_THROWS_AS(j1.get_to(arr6), json::type_error&); + CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.type_error.302] " + "array size must be 6, but is 4"); + } + + SECTION("built-in array is smaller than JSON") + { + json j1 = {1, 2, 3, 4}; + int arr2[] = {8, 9}; + CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); + CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " + "array size must be 2, but is 4"); + } + + SECTION("built-in array from non-array type") + { + json j1; + int arr2[] = {8, 9}; + CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); + CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " + "type must be array, but is null"); + } } SECTION("std::deque") @@ -1615,8 +1642,10 @@ TEST_CASE("value conversion") { CHECK_THROWS_AS((json().get>()), json::type_error&); CHECK_THROWS_AS((json().get>()), json::type_error&); + CHECK_THROWS_AS((json().get>()), json::type_error&); CHECK_THROWS_AS((json().get>()), json::type_error&); CHECK_THROWS_AS((json().get>()), json::type_error&); + CHECK_THROWS_AS((json().get>()), json::type_error&); CHECK_THROWS_AS((json().get>()), json::type_error&); // does type really must be an array? or it rather must not be null? @@ -1627,12 +1656,18 @@ TEST_CASE("value conversion") CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); From 0358aac8ffec84b4f32b6835e6a456b0bc2eb78b Mon Sep 17 00:00:00 2001 From: domsoll <76645975+domsoll@users.noreply.github.com> Date: Thu, 31 Dec 2020 12:49:59 +0100 Subject: [PATCH 4/5] ENH: option to enable strict container size checks added, default is disabled ENH: test cases adapted for strict size checks option MNT: updated amalgamation header --- CMakeLists.txt | 2 + .../nlohmann/detail/conversions/from_json.hpp | 8 +++ include/nlohmann/detail/macro_scope.hpp | 4 ++ single_include/nlohmann/json.hpp | 12 ++++ test/src/unit-constructor1.cpp | 34 +++++++++ test/src/unit-conversions.cpp | 70 +++++++++++++++++-- 6 files changed, 126 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa77a5aed..7e0f3b875 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ON) option(JSON_Install "Install CMake targets during install step." ON) option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF) option(JSON_ImplicitConversions "Enable implicit conversions." ON) +option(JSON_StrictContainerSize "Enable strict size checks for container conversions." OFF) ## ## CONFIGURATION @@ -70,6 +71,7 @@ target_compile_definitions( ${NLOHMANN_JSON_TARGET_NAME} INTERFACE JSON_USE_IMPLICIT_CONVERSIONS=$ + JSON_USE_STRICT_CONTAINER_SIZE=$ ) target_include_directories( diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index b85a4e3d6..36fd72681 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -164,6 +164,7 @@ template auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get(), void()) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -172,6 +173,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); } +#endif for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -189,11 +191,13 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { +#if JSON_USE_STRICT_CONTAINER_SIZE // array type check done in from_json already, only check size if (JSON_HEDLEY_UNLIKELY(j.size() != N)) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); } +#endif for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -339,6 +343,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) template void from_json(const BasicJsonType& j, std::pair& p) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -347,12 +352,14 @@ void from_json(const BasicJsonType& j, std::pair& p) { JSON_THROW(type_error::create(302, "array size must be 2, but is " + std::to_string(j.size()))); } +#endif p = {j.at(0).template get(), j.at(1).template get()}; } template void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -361,6 +368,7 @@ void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence::type>()...); } diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 8c9f63296..ee22f1fd7 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -294,3 +294,7 @@ #else #define JSON_EXPLICIT explicit #endif + +#ifndef JSON_USE_STRICT_CONTAINER_SIZE + #define JSON_USE_STRICT_CONTAINER_SIZE 0 +#endif diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8db2a0eff..ce0e59a2b 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2312,6 +2312,10 @@ JSON_HEDLEY_DIAGNOSTIC_POP #define JSON_EXPLICIT explicit #endif +#ifndef JSON_USE_STRICT_CONTAINER_SIZE + #define JSON_USE_STRICT_CONTAINER_SIZE 0 +#endif + namespace nlohmann { @@ -3564,6 +3568,7 @@ template auto from_json(const BasicJsonType& j, T (&arr)[N]) -> decltype(j.template get(), void()) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -3572,6 +3577,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); } +#endif for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -3589,11 +3595,13 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, priority_tag<2> /*unused*/) -> decltype(j.template get(), void()) { +#if JSON_USE_STRICT_CONTAINER_SIZE // array type check done in from_json already, only check size if (JSON_HEDLEY_UNLIKELY(j.size() != N)) { JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()))); } +#endif for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -3739,6 +3747,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val) template void from_json(const BasicJsonType& j, std::pair& p) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -3747,12 +3756,14 @@ void from_json(const BasicJsonType& j, std::pair& p) { JSON_THROW(type_error::create(302, "array size must be 2, but is " + std::to_string(j.size()))); } +#endif p = {j.at(0).template get(), j.at(1).template get()}; } template void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence /*unused*/) { +#if JSON_USE_STRICT_CONTAINER_SIZE if (JSON_HEDLEY_UNLIKELY(!j.is_array())) { JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); @@ -3761,6 +3772,7 @@ void from_json_tuple_impl(const BasicJsonType& j, Tuple& t, index_sequence::type>()...); } diff --git a/test/src/unit-constructor1.cpp b/test/src/unit-constructor1.cpp index b916a313c..2b958f0fc 100644 --- a/test/src/unit-constructor1.cpp +++ b/test/src/unit-constructor1.cpp @@ -269,6 +269,17 @@ TEST_CASE("constructors") CHECK(j[1] == std::get<1>(p)); } +#if !(JSON_USE_STRICT_CONTAINER_SIZE) + SECTION("std::pair with discarded values") + { + json j{1, 2.0, "string"}; + + const auto p = j.get>(); + CHECK(p.first == j[0]); + CHECK(p.second == j[1]); + } +#endif + SECTION("std::tuple") { const auto t = std::make_tuple(1.0, std::string{"string"}, 42, std::vector {0, 1}); @@ -284,8 +295,21 @@ TEST_CASE("constructors") CHECK(j[3][1] == 1); } +#if !(JSON_USE_STRICT_CONTAINER_SIZE) + SECTION("std::tuple with discarded values") + { + json j{1, 2.0, "string", 42}; + + const auto t = j.get>(); + CHECK(std::get<0>(t) == j[0]); + CHECK(std::get<1>(t) == j[1]); + CHECK(std::get<2>(t) == j[2]); + } +#endif + SECTION("std::pair/tuple/array failures") { +#if JSON_USE_STRICT_CONTAINER_SIZE json j1{1}; json j2{1, 2, 3}; @@ -301,6 +325,16 @@ TEST_CASE("constructors") CHECK_THROWS_WITH((j1.get>()), "[json.exception.type_error.302] array size must be 3, but is 1"); CHECK_THROWS_AS((j2.get>()), json::type_error&); CHECK_THROWS_WITH((j2.get>()), "[json.exception.type_error.302] array size must be 1, but is 3"); +#else + json j{1}; + + CHECK_THROWS_AS((j.get>()), json::out_of_range&); + CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); + CHECK_THROWS_AS((j.get>()), json::out_of_range&); + CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); + CHECK_THROWS_AS((j.get>()), json::out_of_range&); + CHECK_THROWS_WITH((j.get>()), "[json.exception.out_of_range.401] array index 1 is out of range"); +#endif } SECTION("std::forward_list") diff --git a/test/src/unit-conversions.cpp b/test/src/unit-conversions.cpp index 48b410fe7..4c2122294 100644 --- a/test/src/unit-conversions.cpp +++ b/test/src/unit-conversions.cpp @@ -405,18 +405,30 @@ TEST_CASE("value conversion") { json j1 = {1, 2, 3, 4}; int arr6[] = {1, 2, 3, 4, 5, 6}; +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j1.get_to(arr6), json::type_error&); CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.type_error.302] " "array size must be 6, but is 4"); +#else + CHECK_THROWS_AS(j1.get_to(arr6), json::out_of_range&); + CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.out_of_range.401] " + "array index 4 is out of range"); +#endif } SECTION("built-in array is smaller than JSON") { json j1 = {1, 2, 3, 4}; int arr2[] = {8, 9}; +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " "array size must be 2, but is 4"); +#else + j1.get_to(arr2); + CHECK(arr2[0] == 1); + CHECK(arr2[1] == 2); +#endif } SECTION("built-in array from non-array type") @@ -424,8 +436,13 @@ TEST_CASE("value conversion") json j1; int arr2[] = {8, 9}; CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " "type must be array, but is null"); +#else + CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.304] " + "cannot use at() with null"); +#endif } } @@ -1525,17 +1542,29 @@ TEST_CASE("value conversion") SECTION("std::array is larger than JSON") { std::array arr6 = {{1, 2, 3, 4, 5, 6}}; +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j1.get_to(arr6), json::type_error&); CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.type_error.302] " "array size must be 6, but is 4"); +#else + CHECK_THROWS_AS(j1.get_to(arr6), json::out_of_range&); + CHECK_THROWS_WITH(j1.get_to(arr6), "[json.exception.out_of_range.401] " + "array index 4 is out of range"); +#endif } SECTION("std::array is smaller than JSON") { std::array arr2 = {{8, 9}}; +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j1.get_to(arr2), json::type_error&); CHECK_THROWS_WITH(j1.get_to(arr2), "[json.exception.type_error.302] " "array size must be 2, but is 4"); +#else + j1.get_to(arr2); + CHECK(arr2[0] == 1); + CHECK(arr2[1] == 2); +#endif } } @@ -1703,18 +1732,30 @@ TEST_CASE("value conversion") SECTION("std::pair is larger than JSON") { - std::pair p; + std::pair p(8, 9); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j10.get_to(p), json::type_error&); CHECK_THROWS_WITH(j10.get_to(p), "[json.exception.type_error.302] " "array size must be 2, but is 1"); +#else + CHECK_THROWS_AS(j10.get_to(p), json::out_of_range&); + CHECK_THROWS_WITH(j10.get_to(p), "[json.exception.out_of_range.401] " + "array index 1 is out of range"); +#endif } SECTION("std::pair is smaller than JSON") { - std::pair p; + std::pair p(8, 9); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j9.get_to(p), json::type_error&); CHECK_THROWS_WITH(j9.get_to(p), "[json.exception.type_error.302] " "array size must be 2, but is 3"); +#else + j9.get_to(p); + CHECK(p.first == 1); + CHECK(p.second == 2); +#endif } } @@ -1733,18 +1774,30 @@ TEST_CASE("value conversion") SECTION("std::tuple is larger than JSON") { - std::tuple t; + std::tuple t(8, 9); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j10.get_to(t), json::type_error&); CHECK_THROWS_WITH(j10.get_to(t), "[json.exception.type_error.302] " "array size must be 2, but is 1"); +#else + CHECK_THROWS_AS(j10.get_to(t), json::out_of_range&); + CHECK_THROWS_WITH(j10.get_to(t), "[json.exception.out_of_range.401] " + "array index 1 is out of range"); +#endif } SECTION("std::tuple is smaller than JSON") { - std::tuple t; + std::tuple t(8, 9); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_AS(j9.get_to(t), json::type_error&); CHECK_THROWS_WITH(j9.get_to(t), "[json.exception.type_error.302] " "array size must be 2, but is 3"); +#else + j9.get_to(t); + CHECK(std::get<0>(t) == 1); + CHECK(std::get<1>(t) == 2); +#endif } } @@ -1753,12 +1806,21 @@ TEST_CASE("value conversion") CHECK_THROWS_AS((json().get>()), json::type_error&); CHECK_THROWS_AS((json().get>()), json::type_error&); +#if JSON_USE_STRICT_CONTAINER_SIZE CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); CHECK_THROWS_WITH( (json().get>()), "[json.exception.type_error.302] type must be array, but is null"); +#else + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.304] cannot use at() with null"); + CHECK_THROWS_WITH( + (json().get>()), + "[json.exception.type_error.304] cannot use at() with null"); +#endif } } } From 93aabac6efa51425e6d9e0d5e18927fc9e5c72a5 Mon Sep 17 00:00:00 2001 From: domsoll <76645975+domsoll@users.noreply.github.com> Date: Mon, 20 Sep 2021 16:19:47 +0200 Subject: [PATCH 5/5] STY: minor formatting cleanup --- include/nlohmann/detail/conversions/from_json.hpp | 3 +++ single_include/nlohmann/json.hpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/nlohmann/detail/conversions/from_json.hpp b/include/nlohmann/detail/conversions/from_json.hpp index f8ea2afd3..696b26a04 100644 --- a/include/nlohmann/detail/conversions/from_json.hpp +++ b/include/nlohmann/detail/conversions/from_json.hpp @@ -182,6 +182,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines- JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()), j)); } #endif + for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -206,6 +207,7 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()), j)); } #endif + for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -289,6 +291,7 @@ std::array from_json_inplace_array_impl(BasicJsonType&& j, JSON_THROW(type_error::create(302, "array size must be " + std::to_string(sizeof...(Idx)) + ", but is " + std::to_string(j.size()), j)); } #endif + return { { std::forward(j).at(Idx).template get()... } }; } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index e2441ce35..a0391f2f6 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -4053,6 +4053,7 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines- JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()), j)); } #endif + for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -4077,6 +4078,7 @@ auto from_json_array_impl(const BasicJsonType& j, std::array& arr, JSON_THROW(type_error::create(302, "array size must be " + std::to_string(N) + ", but is " + std::to_string(j.size()), j)); } #endif + for (std::size_t i = 0; i < N; ++i) { arr[i] = j.at(i).template get(); @@ -4160,6 +4162,7 @@ std::array from_json_inplace_array_impl(BasicJsonType&& j, JSON_THROW(type_error::create(302, "array size must be " + std::to_string(sizeof...(Idx)) + ", but is " + std::to_string(j.size()), j)); } #endif + return { { std::forward(j).at(Idx).template get()... } }; } @@ -17663,7 +17666,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::parser_callback_tcb = nullptr, const bool allow_exceptions = true, const bool ignore_comments = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), std::move(cb), allow_exceptions, ignore_comments);