mirror of
https://github.com/nlohmann/json.git
synced 2026-09-09 17:58:00 +00:00
* Reserve capacity in from_json() object conversion when supported The object-to-container from_json() overload filled the target container one element at a time without reserving capacity, even when the target type supports reserve() (e.g. std::unordered_map) and the number of elements is already known. This caused unnecessary rehashing while parsing large objects into such containers. Add a reserve-detecting overload (from_json_object_impl), mirroring the priority_tag-based SFINAE technique already used by the array conversion path (from_json_array_impl), so that reserve(size()) is called up front when available and the loop falls back unchanged otherwise (e.g. for std::map). Fixes #5406 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Update doc example outputs for new object-conversion iteration order Reserving capacity in from_json()'s object-conversion path before inserting elements changes libstdc++'s std::unordered_map bucket layout, which changes the iteration order used by get__ValueType_const.cpp, get_to.cpp and operator__ValueType.cpp to print the elements of a converted std::unordered_map<std::string, json>. Verified against a clean develop checkout (built with the same GCC/libstdc++ used in CI) that the old order was produced without this PR's change and the new order is produced with it, and that the three affected examples now match their updated expected output byte-for-byte. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Factor out a reserve-dispatch helper instead of duplicating the object from_json loop Addresses review feedback from @gregmarr on PR #5472: the emplace loop no longer needs to exist twice for the reserve/no-reserve cases. A small from_json_object_reserve() overload pair (SFINAE-dispatched on whether reserve() exists, mirroring the priority_tag technique used elsewhere) either calls reserve() or is a no-op; from_json_object_impl() calls it once. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Inline from_json_object_impl into from_json now that it is called only once Addresses review feedback from @gregmarr on PR #5472: with the reserve loop de-duplicated, from_json_object_impl no longer needs to be a separate function that from_json immediately delegates to. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
1888 lines
65 KiB
C++
1888 lines
65 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | 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 an object (explicit)")
|
|
{
|
|
const json::object_t o_reference = {{"object", json::object()},
|
|
{"array", {1, 2, 3, 4}},
|
|
{"number", 42},
|
|
{"boolean", false},
|
|
{"null", nullptr},
|
|
{"string", "Hello world"}
|
|
};
|
|
json j(o_reference);
|
|
|
|
SECTION("json::object_t")
|
|
{
|
|
json::object_t const o = j.get<json::object_t>();
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::map<json::string_t, json>")
|
|
{
|
|
const std::map<json::string_t, json> o =
|
|
j.get<std::map<json::string_t, json>>();
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::multimap<json::string_t, json>")
|
|
{
|
|
const std::multimap<json::string_t, json> o =
|
|
j.get<std::multimap<json::string_t, json>>();
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_map<json::string_t, json>")
|
|
{
|
|
const std::unordered_map<json::string_t, json> o =
|
|
j.get<std::unordered_map<json::string_t, json>>();
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_multimap<json::string_t, json>")
|
|
{
|
|
const std::unordered_multimap<json::string_t, json> o =
|
|
j.get<std::unordered_multimap<json::string_t, json>>();
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("exception in case of a non-object type")
|
|
{
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::array).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::string).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, "
|
|
"but is boolean", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_integer).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_unsigned).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_float).get<json::object_t>(),
|
|
"[json.exception.type_error.302] type must be object, but is number", json::type_error&);
|
|
}
|
|
}
|
|
|
|
SECTION("get an object (explicit, get_to)")
|
|
{
|
|
const json::object_t o_reference = {{"object", json::object()},
|
|
{"array", {1, 2, 3, 4}},
|
|
{"number", 42},
|
|
{"boolean", false},
|
|
{"null", nullptr},
|
|
{"string", "Hello world"}
|
|
};
|
|
json j(o_reference);
|
|
|
|
SECTION("json::object_t")
|
|
{
|
|
json::object_t o = {{"previous", "value"}};
|
|
j.get_to(o);
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::map<json::string_t, json>")
|
|
{
|
|
std::map<json::string_t, json> o{{"previous", "value"}};
|
|
j.get_to(o);
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::multimap<json::string_t, json>")
|
|
{
|
|
std::multimap<json::string_t, json> o{{"previous", "value"}};
|
|
j.get_to(o);
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_map<json::string_t, json>")
|
|
{
|
|
std::unordered_map<json::string_t, json> o{{"previous", "value"}};
|
|
j.get_to(o);
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_multimap<json::string_t, json>")
|
|
{
|
|
std::unordered_multimap<json::string_t, json> o{{"previous", "value"}};
|
|
j.get_to(o);
|
|
CHECK(json(o) == j);
|
|
}
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
|
|
SECTION("get an object (implicit)")
|
|
{
|
|
const json::object_t o_reference = {{"object", json::object()},
|
|
{"array", {1, 2, 3, 4}},
|
|
{"number", 42},
|
|
{"boolean", false},
|
|
{"null", nullptr},
|
|
{"string", "Hello world"}
|
|
};
|
|
json j(o_reference);
|
|
|
|
SECTION("json::object_t")
|
|
{
|
|
const json::object_t o = j;
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::map<json::string_t, json>")
|
|
{
|
|
const std::map<json::string_t, json> o = j;
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::multimap<json::string_t, json>")
|
|
{
|
|
const std::multimap<json::string_t, json> o = j;
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_map<json::string_t, json>")
|
|
{
|
|
const std::unordered_map<json::string_t, json> o = j;
|
|
CHECK(json(o) == j);
|
|
}
|
|
|
|
SECTION("std::unordered_multimap<json::string_t, json>")
|
|
{
|
|
const std::unordered_multimap<json::string_t, json> o = j;
|
|
CHECK(json(o) == j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("get an array (explicit)")
|
|
{
|
|
const json::array_t a_reference{json(1), json(1u), json(2.2),
|
|
json(false), json("string"), json()};
|
|
json j(a_reference);
|
|
|
|
SECTION("json::array_t")
|
|
{
|
|
const json::array_t a = j.get<json::array_t>();
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::list<json>")
|
|
{
|
|
const std::list<json> a = j.get<std::list<json>>();
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::forward_list<json>")
|
|
{
|
|
const std::forward_list<json> a = j.get<std::forward_list<json>>();
|
|
CHECK(json(a) == j);
|
|
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<std::forward_list<json>>(),
|
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
}
|
|
|
|
SECTION("std::vector<json>")
|
|
{
|
|
const std::vector<json> a = j.get<std::vector<json>>();
|
|
CHECK(json(a) == j);
|
|
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<std::vector<json>>(),
|
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
|
|
#if !defined(JSON_NOEXCEPTION)
|
|
SECTION("reserve is called on containers that supports it")
|
|
{
|
|
// make sure all values are properly copied
|
|
const json j2({1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
|
|
auto v2 = j2.get<std::vector<int>>();
|
|
CHECK(v2.size() == 10);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SECTION("built-in arrays")
|
|
{
|
|
const char str[] = "a string"; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
const int nbs[] = {0, 1, 2}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
|
|
const json j2 = nbs;
|
|
const json j3 = str;
|
|
|
|
auto v = j2.get<std::vector<int>>();
|
|
auto s = j3.get<std::string>();
|
|
CHECK(std::equal(v.begin(), v.end(), std::begin(nbs)));
|
|
CHECK(s == str);
|
|
}
|
|
|
|
SECTION("std::deque<json>")
|
|
{
|
|
const std::deque<json> a = j.get<std::deque<json>>();
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("exception in case of a non-array type")
|
|
{
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::object).get<std::vector<int>>(),
|
|
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::object).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::string).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::boolean).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is boolean", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_integer).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_unsigned).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_float).get<json::array_t>(),
|
|
"[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
}
|
|
}
|
|
|
|
SECTION("get an array (explicit, get_to)")
|
|
{
|
|
const json::array_t a_reference{json(1), json(1u), json(2.2),
|
|
json(false), json("string"), json()};
|
|
json j(a_reference);
|
|
|
|
SECTION("json::array_t")
|
|
{
|
|
json::array_t a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::valarray<json>")
|
|
{
|
|
std::valarray<json> a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::list<json>")
|
|
{
|
|
std::list<json> a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::forward_list<json>")
|
|
{
|
|
std::forward_list<json> a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::vector<json>")
|
|
{
|
|
std::vector<json> a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("built-in arrays")
|
|
{
|
|
const int nbs[] = {0, 1, 2}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
int nbs2[] = {0, 0, 0}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
|
|
const json j2 = nbs;
|
|
j2.get_to(nbs2);
|
|
CHECK(std::equal(std::begin(nbs), std::end(nbs), std::begin(nbs2)));
|
|
}
|
|
|
|
SECTION("built-in arrays: 2D")
|
|
{
|
|
const int nbs[][3] = {{0, 1, 2}, {3, 4, 5}}; // NOLINT(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
int nbs2[][3] = {{0, 0, 0}, {0, 0, 0}}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
|
|
const json j2 = nbs;
|
|
j2.get_to(nbs2);
|
|
CHECK(std::equal(std::begin(nbs[0]), std::end(nbs[1]), std::begin(nbs2[0])));
|
|
}
|
|
|
|
SECTION("built-in arrays: 3D")
|
|
{
|
|
// NOLINTBEGIN(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
const int nbs[][2][3] = {\
|
|
{{0, 1, 2}, {3, 4, 5}}, \
|
|
{{10, 11, 12}, {13, 14, 15}}\
|
|
};
|
|
int nbs2[][2][3] = {\
|
|
{{0, 0, 0}, {0, 0, 0}}, \
|
|
{{0, 0, 0}, {0, 0, 0}}\
|
|
};
|
|
// NOLINTEND(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
|
|
const json j2 = nbs;
|
|
j2.get_to(nbs2);
|
|
CHECK(std::equal(std::begin(nbs[0][0]), std::end(nbs[1][1]), std::begin(nbs2[0][0])));
|
|
}
|
|
|
|
SECTION("built-in arrays: 4D")
|
|
{
|
|
// NOLINTBEGIN(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
const int nbs[][2][2][3] = {\
|
|
{
|
|
\
|
|
{{0, 1, 2}, {3, 4, 5}}, \
|
|
{{10, 11, 12}, {13, 14, 15}}\
|
|
}, \
|
|
{
|
|
\
|
|
{{20, 21, 22}, {23, 24, 25}}, \
|
|
{{30, 31, 32}, {33, 34, 35}}\
|
|
}\
|
|
};
|
|
int nbs2[][2][2][3] = {\
|
|
{
|
|
\
|
|
{{0, 0, 0}, {0, 0, 0}}, \
|
|
{{0, 0, 0}, {0, 0, 0}}\
|
|
}, \
|
|
{
|
|
\
|
|
{{0, 0, 0}, {0, 0, 0}}, \
|
|
{{0, 0, 0}, {0, 0, 0}}\
|
|
}\
|
|
};
|
|
// NOLINTEND(misc-const-correctness,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
|
|
const json j2 = nbs;
|
|
j2.get_to(nbs2);
|
|
CHECK(std::equal(std::begin(nbs[0][0][0]), std::end(nbs[1][1][1]), std::begin(nbs2[0][0][0])));
|
|
}
|
|
|
|
SECTION("std::deque<json>")
|
|
{
|
|
std::deque<json> a{"previous", "value"};
|
|
j.get_to(a);
|
|
CHECK(json(a) == j);
|
|
}
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("get an array (implicit)")
|
|
{
|
|
const json::array_t a_reference{json(1), json(1u), json(2.2),
|
|
json(false), json("string"), json()};
|
|
json j(a_reference);
|
|
|
|
SECTION("json::array_t")
|
|
{
|
|
const json::array_t a = j;
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::list<json>")
|
|
{
|
|
const std::list<json> a = j;
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::forward_list<json>")
|
|
{
|
|
const std::forward_list<json> a = j;
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::vector<json>")
|
|
{
|
|
const std::vector<json> a = j;
|
|
CHECK(json(a) == j);
|
|
}
|
|
|
|
SECTION("std::deque<json>")
|
|
{
|
|
const std::deque<json> a = j;
|
|
CHECK(json(a) == j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("get a string (explicit)")
|
|
{
|
|
const json::string_t s_reference{"Hello world"};
|
|
json j(s_reference);
|
|
|
|
SECTION("string_t")
|
|
{
|
|
const json::string_t s = j.get<json::string_t>();
|
|
CHECK(json(s) == j);
|
|
}
|
|
|
|
SECTION("std::string")
|
|
{
|
|
const std::string s = j.get<std::string>();
|
|
CHECK(json(s) == j);
|
|
}
|
|
#if defined(JSON_HAS_CPP_17)
|
|
SECTION("std::string_view")
|
|
{
|
|
std::string_view const s = j.get<std::string_view>();
|
|
CHECK(json(s) == j);
|
|
}
|
|
#endif
|
|
|
|
SECTION("exception in case of a non-string type")
|
|
{
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::object).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::array).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, "
|
|
"but is boolean", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_integer).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_unsigned).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_float).get<json::string_t>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
}
|
|
|
|
#if defined(JSON_HAS_CPP_17)
|
|
SECTION("exception in case of a non-string type using string_view")
|
|
{
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::null).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::object).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::array).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is boolean", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_integer).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_unsigned).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_float).get<std::string_view>(),
|
|
"[json.exception.type_error.302] type must be string, but is number", json::type_error&);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SECTION("get a string (explicit, get_to)")
|
|
{
|
|
const json::string_t s_reference{"Hello world"};
|
|
json j(s_reference);
|
|
|
|
SECTION("string_t")
|
|
{
|
|
json::string_t s = "previous value";
|
|
j.get_to(s);
|
|
CHECK(json(s) == j);
|
|
}
|
|
|
|
SECTION("std::string")
|
|
{
|
|
std::string s = "previous value";
|
|
j.get_to(s);
|
|
CHECK(json(s) == j);
|
|
}
|
|
#if defined(JSON_HAS_CPP_17)
|
|
SECTION("std::string_view")
|
|
{
|
|
std::string const s = "previous value";
|
|
std::string_view sv = s;
|
|
j.get_to(sv);
|
|
CHECK(json(sv) == j);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SECTION("get null (explicit)")
|
|
{
|
|
std::nullptr_t n = nullptr;
|
|
const json j(n);
|
|
|
|
auto n2 = j.get<std::nullptr_t>();
|
|
CHECK(n2 == n);
|
|
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::string).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::object).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::array).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::boolean).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is boolean", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_integer).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_unsigned).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::number_float).get<std::nullptr_t>(),
|
|
"[json.exception.type_error.302] type must be null, but is number", json::type_error&);
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("get a string (implicit)")
|
|
{
|
|
const json::string_t s_reference{"Hello world"};
|
|
json j(s_reference);
|
|
|
|
SECTION("string_t")
|
|
{
|
|
const json::string_t s = j;
|
|
CHECK(json(s) == j);
|
|
}
|
|
|
|
#if defined(JSON_HAS_CPP_17)
|
|
SECTION("std::string_view")
|
|
{
|
|
std::string_view const s = j.get<std::string_view>();
|
|
CHECK(json(s) == j);
|
|
}
|
|
#endif
|
|
|
|
SECTION("std::string")
|
|
{
|
|
const std::string s = j;
|
|
CHECK(json(s) == j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("get a boolean (explicit)")
|
|
{
|
|
const json::boolean_t b_reference{true};
|
|
json j(b_reference);
|
|
|
|
SECTION("boolean_t")
|
|
{
|
|
auto b = j.get<json::boolean_t>();
|
|
CHECK(json(b) == j);
|
|
}
|
|
|
|
SECTION("uint8_t")
|
|
{
|
|
auto n = j.get<uint8_t>();
|
|
CHECK(n == 1);
|
|
}
|
|
|
|
SECTION("bool")
|
|
{
|
|
const bool b = j.get<bool>();
|
|
CHECK(json(b) == j);
|
|
}
|
|
|
|
SECTION("exception in case of a non-number type")
|
|
{
|
|
CHECK_THROWS_AS(json(json::value_t::string).get<uint8_t>(),
|
|
json::type_error&);
|
|
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::object).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, "
|
|
"but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::array).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(json(json::value_t::string).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, "
|
|
"but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_integer).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, but is "
|
|
"number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_unsigned).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, but is "
|
|
"number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::number_float).get<json::boolean_t>(),
|
|
"[json.exception.type_error.302] type must be boolean, but is "
|
|
"number", json::type_error&);
|
|
}
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("get a boolean (implicit)")
|
|
{
|
|
const json::boolean_t b_reference{true};
|
|
json j(b_reference);
|
|
|
|
SECTION("boolean_t")
|
|
{
|
|
const json::boolean_t b = j;
|
|
CHECK(json(b) == j);
|
|
}
|
|
|
|
SECTION("bool")
|
|
{
|
|
const bool b = j;
|
|
CHECK(json(b) == j);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("get an integer number (explicit)")
|
|
{
|
|
const json::number_integer_t n_reference{42};
|
|
json j(n_reference);
|
|
const json::number_unsigned_t n_unsigned_reference{42u};
|
|
json j_unsigned(n_unsigned_reference);
|
|
|
|
SECTION("number_integer_t")
|
|
{
|
|
auto n = j.get<json::number_integer_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("number_unsigned_t")
|
|
{
|
|
auto n = j_unsigned.get<json::number_unsigned_t>();
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("short")
|
|
{
|
|
auto n = j.get<short>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned short")
|
|
{
|
|
auto n = j.get<unsigned short>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int")
|
|
{
|
|
const int n = j.get<int>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned int")
|
|
{
|
|
auto n = j.get<unsigned int>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("long")
|
|
{
|
|
const long n = j.get<long>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned long")
|
|
{
|
|
auto n = j.get<unsigned long>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("long long")
|
|
{
|
|
auto n = j.get<long long>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned long long")
|
|
{
|
|
auto n = j.get<unsigned long long>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int8_t")
|
|
{
|
|
auto n = j.get<int8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_t")
|
|
{
|
|
auto n = j.get<int16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_t")
|
|
{
|
|
auto n = j.get<int32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_t")
|
|
{
|
|
auto n = j.get<int64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int8_fast_t")
|
|
{
|
|
auto n = j.get<int_fast8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_fast_t")
|
|
{
|
|
auto n = j.get<int_fast16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_fast_t")
|
|
{
|
|
auto n = j.get<int_fast32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_fast_t")
|
|
{
|
|
auto n = j.get<int_fast64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int8_least_t")
|
|
{
|
|
auto n = j.get<int_least8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_least_t")
|
|
{
|
|
auto n = j.get<int_least16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_least_t")
|
|
{
|
|
auto n = j.get<int_least32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_least_t")
|
|
{
|
|
auto n = j.get<int_least64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint8_t")
|
|
{
|
|
auto n = j.get<uint8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint16_t")
|
|
{
|
|
auto n = j.get<uint16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint32_t")
|
|
{
|
|
auto n = j.get<uint32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint64_t")
|
|
{
|
|
auto n = j.get<uint64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint8_fast_t")
|
|
{
|
|
auto n = j.get<uint_fast8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint16_fast_t")
|
|
{
|
|
auto n = j.get<uint_fast16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint32_fast_t")
|
|
{
|
|
auto n = j.get<uint_fast32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint64_fast_t")
|
|
{
|
|
auto n = j.get<uint_fast64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint8_least_t")
|
|
{
|
|
auto n = j.get<uint_least8_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint16_least_t")
|
|
{
|
|
auto n = j.get<uint_least16_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint32_least_t")
|
|
{
|
|
auto n = j.get<uint_least32_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint64_least_t")
|
|
{
|
|
auto n = j.get<uint_least64_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("exception in case of a non-number type")
|
|
{
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::number_integer_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::object).get<json::number_integer_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::array).get<json::number_integer_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::string).get<json::number_integer_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::boolean).get<json::number_integer_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is "
|
|
"boolean", json::type_error&);
|
|
|
|
CHECK_NOTHROW(
|
|
json(json::value_t::number_float).get<json::number_integer_t>());
|
|
CHECK_NOTHROW(
|
|
json(json::value_t::number_float).get<json::number_unsigned_t>());
|
|
}
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("get an integer number (implicit)")
|
|
{
|
|
json::number_integer_t const n_reference{42};
|
|
json j(n_reference);
|
|
json::number_unsigned_t const n_unsigned_reference{42u};
|
|
json j_unsigned(n_unsigned_reference);
|
|
|
|
SECTION("number_integer_t")
|
|
{
|
|
auto n = j.get<json::number_integer_t>();
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("number_unsigned_t")
|
|
{
|
|
auto n = j_unsigned.get<json::number_unsigned_t>();
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("short")
|
|
{
|
|
short const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned short")
|
|
{
|
|
unsigned short const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("int")
|
|
{
|
|
int const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned int")
|
|
{
|
|
unsigned int const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("long")
|
|
{
|
|
long const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned long")
|
|
{
|
|
unsigned long const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("long long")
|
|
{
|
|
long long const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("unsigned long long")
|
|
{
|
|
unsigned long long const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("int8_t")
|
|
{
|
|
int8_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_t")
|
|
{
|
|
int16_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_t")
|
|
{
|
|
int32_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_t")
|
|
{
|
|
int64_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int8_fast_t")
|
|
{
|
|
int_fast8_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_fast_t")
|
|
{
|
|
int_fast16_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_fast_t")
|
|
{
|
|
int_fast32_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_fast_t")
|
|
{
|
|
int_fast64_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int8_least_t")
|
|
{
|
|
int_least8_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int16_least_t")
|
|
{
|
|
int_least16_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int32_least_t")
|
|
{
|
|
int_least32_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("int64_least_t")
|
|
{
|
|
int_least64_t const n = j;
|
|
CHECK(json(n) == j);
|
|
}
|
|
|
|
SECTION("uint8_t")
|
|
{
|
|
uint8_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint16_t")
|
|
{
|
|
uint16_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint32_t")
|
|
{
|
|
uint32_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint64_t")
|
|
{
|
|
uint64_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint8_fast_t")
|
|
{
|
|
uint_fast8_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint16_fast_t")
|
|
{
|
|
uint_fast16_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint32_fast_t")
|
|
{
|
|
uint_fast32_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint64_fast_t")
|
|
{
|
|
uint_fast64_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint8_least_t")
|
|
{
|
|
uint_least8_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint16_least_t")
|
|
{
|
|
uint_least16_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint32_least_t")
|
|
{
|
|
uint_least32_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
|
|
SECTION("uint64_least_t")
|
|
{
|
|
uint_least64_t const n = j_unsigned;
|
|
CHECK(json(n) == j_unsigned);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("get a floating-point number (explicit)")
|
|
{
|
|
json::number_float_t const n_reference{42.23};
|
|
json const j(n_reference);
|
|
|
|
SECTION("number_float_t")
|
|
{
|
|
auto n = j.get<json::number_float_t>();
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
|
|
SECTION("float")
|
|
{
|
|
auto n = j.get<float>();
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
|
|
SECTION("double")
|
|
{
|
|
auto n = j.get<double>();
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
|
|
SECTION("exception in case of a non-string type")
|
|
{
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::null).get<json::number_float_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is null", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::object).get<json::number_float_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is object", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::array).get<json::number_float_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is array", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::string).get<json::number_float_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is string", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(
|
|
json(json::value_t::boolean).get<json::number_float_t>(),
|
|
"[json.exception.type_error.302] type must be number, but is "
|
|
"boolean", json::type_error&);
|
|
|
|
CHECK_NOTHROW(
|
|
json(json::value_t::number_integer).get<json::number_float_t>());
|
|
CHECK_NOTHROW(
|
|
json(json::value_t::number_unsigned).get<json::number_float_t>());
|
|
}
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("get a floating-point number (implicit)")
|
|
{
|
|
json::number_float_t const n_reference{42.23};
|
|
json const j(n_reference);
|
|
|
|
SECTION("number_float_t")
|
|
{
|
|
json::number_float_t const n = j;
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
|
|
SECTION("float")
|
|
{
|
|
float const n = j;
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
|
|
SECTION("double")
|
|
{
|
|
double const n = j;
|
|
CHECK(json(n).m_data.m_value.number_float == Approx(j.m_data.m_value.number_float));
|
|
}
|
|
}
|
|
#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);
|
|
}
|
|
#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")
|
|
{
|
|
j1.get<std::map<std::string, int>>();
|
|
j2.get<std::map<std::string, unsigned int>>();
|
|
j3.get<std::map<std::string, double>>();
|
|
j4.get<std::map<std::string, bool>>();
|
|
j5.get<std::map<std::string, std::string>>();
|
|
}
|
|
|
|
SECTION("std::unordered_map")
|
|
{
|
|
j1.get<std::unordered_map<std::string, int>>();
|
|
j2.get<std::unordered_map<std::string, unsigned int>>();
|
|
j3.get<std::unordered_map<std::string, double>>();
|
|
j4.get<std::unordered_map<std::string, bool>>();
|
|
j5.get<std::unordered_map<std::string, std::string>>();
|
|
// CHECK(m5["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")
|
|
{
|
|
j1.get<std::multimap<std::string, int>>();
|
|
j2.get<std::multimap<std::string, unsigned int>>();
|
|
j3.get<std::multimap<std::string, double>>();
|
|
j4.get<std::multimap<std::string, bool>>();
|
|
j5.get<std::multimap<std::string, std::string>>();
|
|
// CHECK(m5["one"] == "eins");
|
|
}
|
|
|
|
SECTION("std::unordered_multimap")
|
|
{
|
|
j1.get<std::unordered_multimap<std::string, int>>();
|
|
j2.get<std::unordered_multimap<std::string, unsigned int>>();
|
|
j3.get<std::unordered_multimap<std::string, double>>();
|
|
j4.get<std::unordered_multimap<std::string, bool>>();
|
|
j5.get<std::unordered_multimap<std::string, std::string>>();
|
|
// CHECK(m5["one"] == "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")
|
|
{
|
|
j1.get<std::list<int>>();
|
|
j2.get<std::list<unsigned int>>();
|
|
j3.get<std::list<double>>();
|
|
j4.get<std::list<bool>>();
|
|
j5.get<std::list<std::string>>();
|
|
}
|
|
|
|
SECTION("std::forward_list")
|
|
{
|
|
j1.get<std::forward_list<int>>();
|
|
j2.get<std::forward_list<unsigned int>>();
|
|
j3.get<std::forward_list<double>>();
|
|
j4.get<std::forward_list<bool>>();
|
|
j5.get<std::forward_list<std::string>>();
|
|
}
|
|
|
|
SECTION("std::array")
|
|
{
|
|
j1.get<std::array<int, 4>>();
|
|
j2.get<std::array<unsigned int, 3>>();
|
|
j3.get<std::array<double, 4>>();
|
|
j4.get<std::array<bool, 3>>();
|
|
j5.get<std::array<std::string, 3>>();
|
|
|
|
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")
|
|
{
|
|
j1.get<std::valarray<int>>();
|
|
j2.get<std::valarray<unsigned int>>();
|
|
j3.get<std::valarray<double>>();
|
|
j4.get<std::valarray<bool>>();
|
|
j5.get<std::valarray<std::string>>();
|
|
}
|
|
|
|
SECTION("std::vector")
|
|
{
|
|
j1.get<std::vector<int>>();
|
|
j2.get<std::vector<unsigned int>>();
|
|
j3.get<std::vector<double>>();
|
|
j4.get<std::vector<bool>>();
|
|
j5.get<std::vector<std::string>>();
|
|
}
|
|
|
|
SECTION("std::deque")
|
|
{
|
|
j1.get<std::deque<int>>();
|
|
j2.get<std::deque<unsigned int>>();
|
|
j2.get<std::deque<double>>();
|
|
j4.get<std::deque<bool>>();
|
|
j5.get<std::deque<std::string>>();
|
|
}
|
|
|
|
SECTION("std::set")
|
|
{
|
|
j1.get<std::set<int>>();
|
|
j2.get<std::set<unsigned int>>();
|
|
j3.get<std::set<double>>();
|
|
j4.get<std::set<bool>>();
|
|
j5.get<std::set<std::string>>();
|
|
}
|
|
|
|
SECTION("std::unordered_set")
|
|
{
|
|
j1.get<std::unordered_set<int>>();
|
|
j2.get<std::unordered_set<unsigned int>>();
|
|
j3.get<std::unordered_set<double>>();
|
|
j4.get<std::unordered_set<bool>>();
|
|
j5.get<std::unordered_set<std::string>>();
|
|
}
|
|
|
|
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;
|
|
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&);
|
|
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;
|
|
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&);
|
|
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,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - 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,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - 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,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - 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,cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) - false positive
|
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState,
|
|
{
|
|
{STRICT_TS_INVALID, nullptr},
|
|
{STRICT_TS_STOPPED, "stopped"},
|
|
{STRICT_TS_RUNNING, "running"},
|
|
{STRICT_TS_COMPLETED, "completed"},
|
|
})
|
|
|
|
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(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&);
|
|
}
|
|
|
|
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(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&);
|
|
}
|
|
}
|
|
|
|
|
|
#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
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
#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
|