mirror of
https://github.com/nlohmann/json.git
synced 2026-10-02 06:15:46 +01:00
* Run the README test case in JSON_FastTests jobs The "README" test case was marked doctest::skip() when the tests moved from Catch to doctest in 2019, where it replaced Catch's hidden tag. It is not slow (17 assertions, about 0.00 s), but cmake/test.cmake only passes --no-skip when JSON_FastTests is off, so the per-compiler ci_test_*_cxxNN matrix, macOS, Windows Release/ARM, icpc, icpx and nvhpc compiled the README examples without running them. Drop the skip decorator so every job runs the case. Test-only change. Part of #5713 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove stale clang ranges guards in unit-iterators2.cpp The "algorithms" and "views" sections were guarded by clang/libstdc++ checks written for a clang 15 (04/2022) bug. The first guard's condition contradicts its own comment: it skips clang+libc++ and keeps clang+libstdc++. Both sections already sit inside `#if JSON_HAS_RANGES`, which macro_scope.hpp excludes for the toolchains these guards targeted, so the inner guards never let the sections run on the platforms they meant to protect and are redundant on the rest. Verified locally with Apple clang 21/libc++ and clang 16.0.6/libstdc++ 12 (Docker): both pass all 1355 assertions with the guards removed. Part of #5713 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix copy-pasted CBOR half-float checks; enable stale encode checks In the RFC 8949 Appendix A test case, the decode checks for 5.960464477539063e-8 (0xf9 0x00 0x01) and 0.00006103515625 (0xf9 0x04 0x00) were copy-pasted from the neighboring -4.0 example, so those two half-float byte sequences were never actually decoded and checked, and -4.0 was checked three times instead. The two float32 encode checks for 100000.0 and 3.4028234663852886e+38 were commented out before the writer supported emitting float32 and are now verified to match byte for byte, so they are enabled. The remaining commented-out half-precision to_cbor checks are collapsed into a single explanatory comment, since the writer never emits half-precision floats. Part of #5713 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Assert on the result of STL container conversions in tests The "object-like STL containers" and "array-like STL containers" sections converted json values into std::map, unordered_map, multimap, unordered_multimap, list, forward_list, array, valarray, vector, deque, set and unordered_set and discarded the result, so these ~60 conversions only proved that the code compiles and does not throw; a conversion that dropped or reordered elements would still pass. Bind each result and compare it against the expected container. Also fix a copy-paste slip in the deque section (`j2.get<std::deque<double>>()` instead of j3, so j3's doubles were never converted to a deque), and remove the dead `// CHECK(m5["one"] == "eins")` comments that referred to a variable that did not exist by asserting the equivalent through the bound result. Part of #5713 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Deduplicate SaxCountdown and other test helpers across formats SaxCountdown was copied byte-for-byte into six binary-format test files (unit-cbor.cpp, unit-msgpack.cpp, unit-ubjson.cpp, unit-bjdata.cpp, unit-bon8.cpp, unit-bson.cpp), about 370 redundant lines. Move it into tests/src/sax_countdown.hpp (namespace utils, alongside test_utils.hpp and round_trip_corpus.hpp) and include it from all six. trait_test_arg and the "value_in_range_of trait" TEST_CASE_TEMPLATE_DEFINE were duplicated between unit-32bit.cpp and unit-bjdata.cpp; the trait is a detail/meta trait, not specific to either file. Move it into tests/src/value_in_range_of_test.hpp; unit-32bit.cpp keeps its own include, since JSON_32bitTest=ONLY builds only that file. Each file keeps its own TEST_CASE_TEMPLATE_INVOKE list. sax_no_exception and the "issue #2824" section were duplicated in unit-regression2.cpp and unit-disabled_exceptions.cpp. Drop the copy from unit-regression2.cpp; unit-disabled_exceptions.cpp already covers the no-exceptions case that #2824 was about, and ci_test_noexceptions reruns it. No behavior change. Verified by building and running unit-cbor, unit-msgpack, unit-ubjson, unit-bjdata, unit-bon8, unit-bson, unit-32bit, unit-regression2 and unit-disabled_exceptions against include/ (clang++ -std=c++11, ASan/UBSan where applicable); assertion counts are unchanged from before the refactor. Part of #5714 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the unreferenced vendored libFuzzer tests/thirdparty/Fuzzer (155 files, ~776 KB of vendored Apache-2.0 LLVM code from the 2016 OSS-Fuzz import) is not referenced by any CMakeLists, Makefile or workflow: the fuzz drivers link against -fsanitize=fuzzer or the repo's own tests/src/fuzzer-driver_afl.cpp. Its vendored README only points at llvm.org's own libFuzzer docs. Being dead code, it also adds noise to the flawfinder code-scanning workflow, which scans the whole tree. Remove the directory and its .reuse/dep5 entry. Part of #5714 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove unreferenced 2016 benchmark and fuzz reports tests/reports (1.6 MB) holds AFL status pages and plots from 2016-08-29 and 2016-10-02, and a nativejson-benchmark snapshot from 2016 with links to rawgit.com, which shut down in 2019. Nothing references this directory: no doc, README section, script or workflow points at it, and it describes a ten-years-old, pre-2.0 snapshot of the library. Part of #5714 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Run the CBOR, MessagePack, BSON and BON8 round-trip invariants in CI tests/src/round_trip_corpus.hpp exists so that the byte-stability invariant the fuzzer drivers check also runs on a fixed corpus in CI, instead of only at OSS-Fuzz. So far only the UBJSON and BJData drivers had a matching unit test; the CBOR, MessagePack, BSON and BON8 drivers assert the same invariant (assert(to_X(j2) == vec)) but nothing ran it outside OSS-Fuzz. Add "<FORMAT> round-trip invariants" test cases to unit-cbor.cpp, unit-msgpack.cpp, unit-bson.cpp and unit-bon8.cpp, modeled on the UBJSON case: seed j1 from the corpus (skipping values that do not survive the format's own round trip, as the fuzzer drivers only ever see values from_X() actually produced), then require from_X(to_X(j1)) not to throw and check to_X(j2) == to_X(j1). BSON only serializes objects, so non-object corpus values are skipped. Update the comments in round_trip_corpus.hpp and tests/fuzzing.md to name all six formats. The stream-versus-contiguous check in the BON8 driver is left out, as #5601 reworks it. A local probe confirms no violations on the current corpus (CBOR 3849 checked, MessagePack 3909, BSON 2958, BON8 3841 - matching the counts already recorded for this probe in the issue). Closes #5714 item 1. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix fuzzer driver step lists to match the checks the code performs The header comment of six of the seven binary-format fuzzer drivers listed an invariant the code does not check: CBOR, MessagePack, BSON and BON8 said "assert(j1 == j2)", but the code checks byte stability, assert(to_X(j2) == vec). UBJSON and BJData still described the old "assert(j1 == j2/j3/j4)" byte-exact check from before PR #5494 replaced it with a use_size/use_type-aware round trip (UBJSON) and a value-stability check (BJData); BJData's added paragraph already explained the new check, but the step list above it did not. Also remove a dead branch in fuzzer-parse_bson.cpp: from_bson() is called with allow_exceptions = true, so it throws instead of returning a discarded value, and the "if (j1.is_discarded()) return 0;" guard could never trigger. Drop the unused <iostream> include from all seven drivers and <sstream> from all but fuzzer-parse_bon8.cpp, which is the only one that uses std::istringstream. Overlaps #5601, which edits all seven drivers in the same hunks. Closes #5714 item 4. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Silence the CMP0169 deprecation in cmake_fetch_content, fix stale guards tests/cmake_fetch_content/project calls the single-argument FetchContent_Populate(json) after FetchContent_Declare(), which CMake 3.30 deprecated as CMP0169. Since the project declares cmake_minimum_required(VERSION 3.11...3.14), the policy stays unset, so every configure with a current CMake prints the deprecation warning. The test is kept on purpose: it is the only coverage of the FetchContent_Populate + add_subdirectory pattern for CMake 3.11-3.13 users, which the docs still describe as supported. Explicitly set CMP0169 to OLD, with a comment explaining why. Also fix two stale version guards: - tests/cmake_fetch_content/CMakeLists.txt guarded the test with VERSION_GREATER "3.11.0", which is dead now that tests/CMakeLists.txt requires CMake 3.13. - tests/cmake_fetch_content2/CMakeLists.txt guarded with VERSION_GREATER "3.14.0", which skips exactly 3.14.0, the first version with FetchContent_MakeAvailable. Change it to VERSION_GREATER_EQUAL "3.14". Verified locally: `ctest -R cmake_fetch_content` passes with CMake 4.1, and the CMP0169 deprecation warning that appeared before this change is gone. Closes #5714 item 5. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make the CMake integration-test wrappers consistent The six tests/cmake_* integration-test wrappers had drifted: - Only cmake_import and cmake_import_minver forwarded -A "${CMAKE_GENERATOR_PLATFORM}" to the inner configure, and none forwarded -T "${CMAKE_GENERATOR_TOOLSET}". The Windows workflow configures the outer build with -A Win32 -T ClangCL, so without forwarding, the inner projects of cmake_add_subdirectory, cmake_fetch_content, cmake_fetch_content2 and cmake_target_include_directories built with the generator defaults instead of matching the outer build's platform and toolset. Forward both consistently from all six wrappers. - cmake_fetch_content and cmake_fetch_content2 passed -Dnlohmann_json_source to their inner projects, which never read it (CMake warns "manually-specified variables were not used"); the inner projects fetch their own copy of the library instead. Drop it. - tests/CMakeLists.txt set JSON_FORCED_GLOBAL_COMPILE_OPTIONS from the matching environment variable but never read the cache variable again; the lines right below it read $ENV{JSON_FORCED_GLOBAL_COMPILE_OPTIONS} directly, like the LINK_OPTIONS counterpart already does. Remove the dead set(). This changes which platform and toolset the Win32 and ClangCL CI jobs build the four newly-forwarding wrappers' inner projects with, which may surface new failures there; CI has to confirm those jobs. Verified locally with Ninja (empty -A ""/-T "" is accepted): all 12 cmake_* tests still pass, and the inner fetch_content configures no longer warn about the unused variable. Closes #5714 item 6. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Turn the #972 fifo_map regression test into a real test The #972 regression test in unit-regression1.cpp only built a my_json array from a string literal (the original crash) and had no CHECK, so the fifo_map object type it exists to demonstrate was never exercised. Meanwhile the docs recommend fifo_map for keeping object keys in insertion order (object_order.md, template_parameters.md), and nothing tested that recommendation. Extend the section: after the original array assignment, parse an object with my_json::parse() (not via the "..."_json UDL, which returns a plain nlohmann::json and would exercise the cross-basic_json conversion constructor instead of the parser's own key insertion - and, as tried locally, does not keep fifo order for this stateful comparator) and check that dump() keeps insertion order, and that it survives erase() and inserting a new key. Also narrow thirdparty/fifo_map off the include path of every other test-* target: it was a PUBLIC include directory of test_main, even though unit-regression1.cpp is its only user. Add a small fifo_map_include INTERFACE library with that include directory and attach it to test-regression1 only via json_test_set_test_options(). Verified locally (test-regression1_cpp11, default build and -fsanitize=address,undefined): the new checks pass; `git grep fifo_map tests` still only finds unit-regression1.cpp and the vendored header. Closes #5714 item 7. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Document the vendored doctest.h patch; fix stale doctest_compatibility.h comments tests/thirdparty/doctest/doctest.h is doctest 2.4.12, imported in #4771. Two weeks later, #4801 hand-edited translateActiveException() to declare "String res;" inside the translator loop instead of before it, so a translator that does not match does not leave a previous translator's result in "res" for the next iteration to see. Nothing recorded this, so re-vendoring doctest.h from upstream would silently drop the fix. Add a comment at the patched site naming the version, the PR and the reason, so a future re-vendor knows to re-apply it. Also fix two stale comments in doctest_compatibility.h: - The DOCTEST_THREAD_LOCAL comment referenced Xcode 6/7, which is no longer supported; reword it to explain why the define must stay regardless (it keeps doctest's own thread_local usage out of the way of the same Clang/MinGW crash that JSON_NO_THREAD_LOCAL works around in the library, see ci_test_no_thread_local). - The <iosfwd> include's comment justified it with tests that define "private" as "public"; no test under tests/src does that any more (removed by #2352). Reword the comment instead of dropping the include, since confirming it is safe to drop needs the full CI matrix including MSVC 2015+. Verified locally that tests/src/unit-readme.cpp still builds and passes 17/17 with these headers. Closes #5714 item 9. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Stop compiling unit-wstring.cpp out entirely on classic ICC tests/src/unit-wstring.cpp wrapped the whole file in #ifndef __INTEL_COMPILER, with the comment "ICPC errors out on multibyte character sequences in source files". The ci_icpc job (intel/oneapi-hpckit:2023.2.1) still exists, so that job ran none of the wstring/u16string/u32string input adapter tests, including the malformed-input checks #5704 (open) extends. Only 9 lines contained non-ASCII bytes: the three *_is_utf16()/ *_is_utf32() probe functions, and three std::wstring/u16string/ u32string literals plus their narrow-string dump() expectations. Rewrite all of them with \u/\U escapes in the wide/u16/u32 literals and \x escapes (split into separate string-literal tokens so a following byte is never read as part of the same hex escape, e.g. "\xE1\x83\x85" "a") in the narrow ones. Remove the #ifndef __INTEL_COMPILER/#endif guard along with it. The *_is_utf16()/*_is_utf32() probes compared a raw multibyte literal against an escape-based one to detect a compiler that misreads the source file's encoding; with no raw literals left to misread, the comparison is now tautological, so drop the probes and the "if" guards around each SECTION's body instead of leaving them in as dead checks. The same non-ASCII-in-source-and-in-a-narrow-comparison pattern existed once more in unit-deserialization.cpp's "Using _json with char8_t literals #4945" test: a raw emoji character in a u8R"(...)" literal, guarded by a check_utf8() that returned false for ICC (same reason) and for Windows without the active UTF-8 code page. Rewrite the literal with a \U escape and compare it against a \x-escaped expectation instead of a second raw literal, and drop check_utf8() and the now-unused <windows.h> include along with the guard. Verified locally (clang, -std=c++11 and -std=c++20, -fsanitize=address,undefined, and a plain build): test-wstring keeps 18/18 assertions and unit-deserialization keeps 466/466 (c++11) and 477/477 (c++20) assertions, matching this branch before the change exactly - no coverage was gained or lost, only the source-encoding dependency was removed. ci_icpc has to confirm classic ICC actually builds and passes test-wstring now; if it does not, that is a real finding, not a reason to restore the guard. Overlaps #5704 (open), which edits unit-wstring.cpp inside the previously-guarded region (an include near the top, checks in the invalid-string sections, and a new section at the end). Closes #5713 item 5. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
1978 lines
73 KiB
C++
1978 lines
73 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);
|
|
}
|
|
|
|
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;
|
|
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&);
|
|
|
|
// 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(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
|
|
|
|
// 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);
|
|
}
|
|
|
|
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
|