mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 05:00:30 +00:00
* Drop stale LCOV_EXCL_LINE from the json_pointer out_of_range.410 throw The comment said the size_type overflow check in array_index() is only triggered on special platforms like 32-bit, and the throw was excluded from coverage. On 64-bit platforms the check is true for SIZE_MAX itself, and unit-json_pointer.cpp has asserted that case four times since #5395, so the line is executed in the coverage job. Reword the comment and remove the exclusion marker so the coverage report notices if the tests stop reaching it. Part of #5725 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Name all three C-array check aliases in the enum-macro NOLINTs NLOHMANN_JSON_SERIALIZE_ENUM(_STRICT) suppressed the c-array warning under modernize-avoid-c-arrays only, but clang-tidy emits the same diagnostic under the aliases cppcoreguidelines-avoid-c-arrays and hicpp-avoid-c-arrays too. Any user running those checks got a false positive at every macro expansion, and our own tests needed a local NOLINT at each call site to work around it. Name all three aliases in the four macro comments instead, and drop the now-redundant c-array names from the five test call-site NOLINTs. Comment-only change; behavior, the public API, and the ABI do not change. Ran make amalgamate. Part of #5725 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Include doctest as a SYSTEM directory instead of disabling warnings for all tests test_main added -Wno-deprecated and -Wno-float-equal as PUBLIC compile options for every non-MSVC compiler, so they were applied to every translation unit, library headers included, and silenced the CI warnings meant to check the library's own -Wfloat-equal pragmas. The only code that actually needed the suppression was the vendored doctest.h, which was included as a normal (non-SYSTEM) directory. Include thirdparty/doctest as SYSTEM for test_main, matching what tests/abi/CMakeLists.txt already does, and drop the two suppressions from both targets. Verified locally that unit-comparison, unit-conversions and unit-constructor1 compile clean with -Werror -Weverything and doctest as -isystem, and that CMake still configures with JSON_BuildTests=ON. Part of #5725 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the no-op ci_clang_analyze target ci_clang_analyze configured the build with the real compiler and only then wrapped ninja with scan-build. scan-build intercepts compiles by overriding CC/CXX, but build.ninja already had the compiler path baked in from the configure step, so every run bypassed the analyzer: CI logs show "No bugs found" after a normal build, never an analysis. The job also used Debian's frozen clang-tools-14 rather than the image's own clang, and CLANG_ANALYZER_CHECKS still named three valist.* checkers that current clang merged into security.VAList. ci_clang_tidy already runs every clang-analyzer-* check (via .clang-tidy's "Checks: '*'") with warnings as errors, so nothing is lost by removing the dead job. Delete ci_clang_analyze, CLANG_ANALYZER_CHECKS and the SCAN_BUILD_TOOL lookup from cmake/ci.cmake, drop it from the ubuntu.yml ci_static_analysis_clang matrix, and drop the now-unused clang-tools apt package (iwyu stays for ci_single_binaries). Reword quality_assurance.md and assurance_case.md, which described the dead job as a working control, to say the Clang Static Analyzer checks run through clang-tidy. Verified that `cmake -DJSON_CI=ON` still configures cleanly and that ci_clang_analyze no longer appears in the generated build or in any CMake/workflow file. Part of #5725 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Re-enable portability-template-virtual-member-function; remove redundant forwards .clang-tidy disabled three checks "to get the CI going" (#4489, 2024-11-13): portability-template-virtual-member-function, bugprone-use-after-move and its alias hicpp-invalid-access-moved. portability-template-virtual-member-function only flagged output_stream_adapter::write_character/write_characters; annotate both with NOLINT and re-enable the check. bugprone-use-after-move flagged several double forwards that have no effect at runtime: - from_json.hpp calls std::forward<BasicJsonType>(j).at(Idx) inside pack expansions; at() has no ref-qualified overloads and always returns an lvalue reference, so the forward is a no-op. Replace with plain j.at(Idx) in all four places. - the move constructor forwards the whole object to its base class and then reads other's members. That is item 9 of #5724 (together with its cppcheck suppressions) and is left to that change. - input_adapters.hpp forwards the container twice on purpose, so the begin/end iterator types match adapter_type; annotate with NOLINT and a comment instead of changing behavior. The check still flags the move constructor (see above) and two sites in at(KeyType&&) (both overloads, json.hpp, in the throw's string_t(std::forward<KeyType>(key)) after find(std::forward<KeyType>(key))). Open PR #5689 rewrites that hunk, so bugprone-use-after-move (and hicpp-invalid-access-moved) stay disabled for now, with a comment explaining why; re-enable them once #5689 and the #5724 move-constructor change have landed. Also resolve the portability-avoid-pragma-once TODO: single_include never has #pragma once (amalgamate.py strips it) and every supported compiler accepts it in include/, so keep it disabled with an explanatory comment instead of a TODO. Fix the stale "json.hpp, around line 1265" comment in unit-class_parser.cpp, which now points at the move constructor's actual line. Behavior, the public API and the ABI do not change. Verified with clang-tidy 22.1.8 that portability-template-virtual-member-function now reports nothing, that bugprone-use-after-move/ hicpp-invalid-access-moved report only the known at(KeyType&&) and move-constructor sites, and that unit-custom-base-class, unit-constructor1, unit-conversions, unit-element_access2, unit-class_parser and unit-diagnostic-positions (JSON_DIAGNOSTIC_POSITIONS=1) compile under ASan/UBSan and pass with the same assertion counts as before. Ran make amalgamate. Part of #5725 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix stale and malformed NOLINT comments json_sax.hpp named "-warnings-as-errors" in the NOLINT list on the two JSON_ASSERT(false) lines; that is the suffix clang-tidy appends to a diagnostic tag under WarningsAsErrors, not a check name, and every other JSON_ASSERT(false) omits it. unit-capacity.cpp carried 30 "// NOLINT(misc-const-correctness)" comments on "json j = ...;" declarations that are all used with non-const members afterwards, so the check has nothing to report there. unit-constructor2.cpp used a blanket "// NOLINT: access after move is OK here" on a use-after-move that hides every check on the line; naming bugprone-use-after-move and hicpp-invalid-access-moved keeps the intent once those checks are re-enabled (#5724). Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 10 * Remove stale .clang-tidy entries -google-runtime-references disabled a check that neither clang-tidy 22.1.8 nor 23.1.2 lists under --list-checks -checks='*'; it was removed upstream. The commented-out HeaderFilterRegex line has been unused since the active HeaderFilterRegex was introduced in #2561 (2021). Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 11 * Remove the GCC C++20 -Wignored-attributes pragma in json.hpp The pragma (added in #5164) claimed to work around the C++ modules redefinition errors of #5103, but #5103 is about hard errors (e.g. "redefinition of std::__is_constant_evaluated()", conflicting std::integral_constant) that ignoring a warning cannot suppress; they are traced to GCC PR 124430 and reproduce with <map> or <string> instead of json.hpp too. A GCC 16.2 -std=gnu++20 -fmodules build following #5103's repro steps still fails with the pragma in place, and a build of all test TUs with GCC_CXXFLAGS (which enable -Wignored-attributes) and the pragma removed produces no such warning. The block only hid a warning class from GCC C++20 users while suggesting #5103 was handled. Overlaps #5610, whose hunks touch the closing half of this pragma to insert the json_literals.hpp include. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 9 * Fix stale doxygen comments hidden by the -Wdocumentation pragma macro_scope.hpp ignores -Wdocumentation and -Wdocumentation-unknown-command for the whole library, which also hides genuine documentation mistakes: - detail::unescape() documented "@return unescaped string" but returns void and unescapes its argument in place; reworded to "@param[in,out] s string to unescape in place" and dropped the bogus @return. - basic_json::get()'s copy-conversion overload wrote "converted to @tparam ValueType" inside @return, which Doxygen and Clang parse as a second, malformed @tparam; changed to "@a ValueType", matching the two other get() overloads a few lines above that already use it. This narrows the gap the -Wdocumentation pragma needs to cover; fully replacing the Doxygen-only commands it also hides (item 2c) is left for after #5267. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 2 * Fix -Wextra-semi-stmt at its actual source, not assert() clang_flags.cmake blamed the global -Wno-extra-semi-stmt on assert(), but assert() expands to an expression under glibc and libc++ and does not trigger this warning. unit-assert_macro.cpp overrides JSON_ASSERT with "{if (!(x)) ++assert_counter; }", a bare block followed by a semicolon at every JSON_ASSERT(...) call site in the library; that was the actual source of 151 of the 208 -Wextra-semi-stmt sites found in a Clang 22 -Weverything sweep of the test suite with the flag removed. Switched to the standard do/while(false) macro idiom, which does not expand to a statement-plus-semicolon, and corrected the comment to name the remaining source instead: vendored Doctest's CAPTURE(x) shim, which already ends in a semicolon. Verified with clang++ -Wextra-semi-stmt (plus the file's other CI ignores) that unit-assert_macro.cpp now compiles without any -Wextra-semi-stmt diagnostic. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 8 (step 1 of 2; step 2 covers the CAPTURE() call sites) * Drop the redundant semicolon from CAPTURE() call sites; remove -Wno-extra-semi-stmt doctest_compatibility.h defines CAPTURE(x) as DOCTEST_CAPTURE(x); (with a trailing semicolon baked into the macro), specifically so call sites do not need to add one themselves; most of the ~267 call sites already follow that convention. The remaining 64 call sites across 20 files wrote "CAPTURE(x);" anyway, turning into a statement plus an empty statement and triggering -Wextra-semi-stmt. Dropped the redundant semicolon at each of those sites. With item 6 having already made vendored Doctest a SYSTEM include, and this the last known source of -Wextra-semi-stmt findings, removed the flag from clang_flags.cmake entirely. Verified with clang++ -Wextra-semi-stmt (plus the file's other CI ignores) that all 20 touched files, plus a file with no CAPTURE() use (unit-json_pointer.cpp), compile without any -Wextra-semi-stmt diagnostic. Signed-off-by: Niels Lohmann <mail@nlohmann.me> #5725 item 8 (step 2 of 2) * Switch ci_static_analysis_clang off the frozen LLVM 22 dev image ubuntu.yml pinned the clang-tidy/clang-tidy-sanitizer/single-binaries job to silkeh/clang:dev, a tag last pushed 2026-02-18 that reports "clang version 22.0.0 (...+20251015...)", a pre-release snapshot from before the LLVM 22 release; the maintainer now updates dev-unstable, 22, and latest instead. Switched to silkeh/clang:22, matching the other clang jobs on :latest. Verified with clang-tidy 22.1.8 (the image's actual version) against this repository's .clang-tidy and library headers what the release image newly reports compared to :dev: - readability-redundant-typename fires at ~250 sites across the _cpp20-relevant conversion/to_chars headers; the library targets C++11 and keeps the typenames, so the check is disabled in .clang-tidy, matching how the file already handles checks that don't fit a C++11 codebase. - misc-anonymous-namespace-in-header fires on the two anonymous namespaces in from_json.hpp and to_json.hpp; added the alias to their existing NOLINT (cert-dcl59-cpp, fuchsia-header-anon-namespaces, google-build-namespaces). - bugprone-std-namespace-modification fires on every addition to namespace std: the std::hash, std::formatter and std::swap overloads in json.hpp, and the std::tuple_size/std::tuple_element specializations in iteration_proxy.hpp (this last file is not named in #5725's item 5, found by actually running clang-tidy 22.1.8 against the current tree). All six are legal, deliberate additions to namespace std (explicit/partial specializations of std types, or the pre-C++20 std::swap overload); annotated each with the check name next to its existing cert-dcl58-cpp NOLINT. - modernize-avoid-c-style-cast reported nothing new. Also added clang++-22/21, clang-tidy-22/21, g++-16 and gcov-16 to the find_program search lists in ci.cmake so a local "maximal warnings" configure prefers the current toolchain version over an older one on PATH. #5725 item 5 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Regenerate cmake/gcc_flags.cmake for GCC 16.2.0 GCC_CXXFLAGS was generated for GCC 15.1.0, but ci_test_gcc and ci_test_gcc_cxx{11..26} now run in gcc:latest, currently GCC 16.2.0, so the "maximal warnings" job was missing warnings introduced since 15.1.0 while carrying entries GCC 16 treats as duplicates or no-ops. Regenerated with https://github.com/nlohmann/gcc_flags (patched locally to not crash on an option whose "-x c++ <opt> -" probe fails before it reads stdin, e.g. -Wabi=; the tool otherwise raises BrokenPipeError instead of recording the option as an error) run against g++ 16.2.0 in the official gcc:16 Docker image, keeping the documented -Wno-* exclusions and the same alphabetical placement scheme as before. Also added three GCC 16 warnings the generator cannot discover on its own because it only probes value ranges/lists it finds in the -Q option name itself, not in the enum choices --help=warnings documents separately: - -Wbidi-chars=any, -Wleading-whitespace=spaces: manually verified these compile cleanly with g++ 16.2.0. - -Wstrict-flex-arrays: deliberately NOT added, unlike the other two. Without -fstrict-flex-arrays (which the library does not enable, as it would change codegen for flexible array members), GCC prints "'-Wstrict-flex-arrays' is ignored when '-fstrict-flex-arrays' is not present" on every translation unit, and under our -Werror that note itself aborts the build. This differs from the harmless no-op warnings already kept in the file (-Whsa, -Wsynth, -Wunreachable-code, -Wunsafe-loop-optimizations), which emit nothing; #5725 item 7 named -Wstrict-flex-arrays as one of the flags GCC 16 adds, but did not anticipate this failure mode. Verified: compiled the library header and a representative set of test translation units (including ones touched by items 1, 3, 8, 9, 10 of this issue) with the regenerated GCC_CXXFLAGS plus -Werror under g++ 16.2.0 at -std=c++11 through -std=c++26, with zero warnings; ran the full local test suite (129/129 passing, unrelated to this compiler) as a regression check. CI must still confirm the actual ci_test_gcc / ci_test_standards_gcc targets end to end, since this was verified with direct g++ invocations rather than through the CMake/ CXXFLAGS environment-variable plumbing in ci.cmake. #5725 item 7 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid std::basic_string<CharType> for non-character output_adapter CharType output_adapter<CharType, StringType> defaulted StringType to std::basic_string<CharType>, and (with JSON_NO_IO undefined) always declared a std::basic_ostream<CharType>&-taking constructor. For CharType with no non-deprecated std::char_traits specialization (only std::uint8_t is ever used this way, by the binary writers), simply naming either type - as an unused default template argument, or as an unused, never-called constructor's parameter type - instantiates std::char_traits<CharType> merely to name it, which some standard libraries mark deprecated: with the library-wide -Wdocumentation pragma (item 2's other half, left for a later commit) temporarily removed, an Apple clang 21 / libc++ TU calling json::to_cbor(j, vec) with std::vector<std::uint8_t>& got one -Wdeprecated-declarations warning per binary writer at the old output_adapters.hpp:193. Replaced the eager std::basic_string<CharType> / std::basic_ostream <CharType> defaults with a bool-tagged partial specialization (not std::conditional, which requires naming both branches' types up front regardless of which is selected, reproducing the same warning) that only ever names std::basic_string<CharType> / std::basic_ostream <CharType> when CharType is actually one of char, wchar_t, char16_t, char32_t, or (with __cpp_lib_char8_t) char8_t. For any other CharType, output_adapter's StringType and ostream-constructor parameter fall back to two distinct empty placeholder types, kept distinct so the two constructor overloads do not collide into a single redeclaration. Public API / behavior: passing a std::basic_string<std::uint8_t>& or std::basic_ostream<std::uint8_t>& directly to a binary writer's output_adapter now fails to compile instead of compiling with a deprecation warning; this was neither documented nor tested. All documented uses (std::vector<CharType>, std::basic_ostream<CharType> and StringType for character CharType) are unaffected. Verified with Apple clang 21 / libc++, with the two -Wdocumentation* "ignored" pragma lines in macro_scope.hpp temporarily removed and -std=c++11/c++20 plus the project's -Weverything flag set: calling to_cbor/to_msgpack/to_ubjson/to_bjdata/to_bson/to_bon8 on a std::vector<std::uint8_t> now produces no char_traits<unsigned char> (or any other) deprecation warning, while the char-based string- and ostream-adapter paths, and a to_cbor/from_cbor round trip, still compile and run correctly; also verified with GCC 16.2.0. Ran the full local test suite, including the binary-format unit tests (unit-cbor, unit-msgpack, unit-ubjson, unit-bjdata, unit-bson, unit-bon8, unit-binary_writer_sinks, unit-binary_formats, unit-custom-binary-type): 129/129 passing. #5725 item 2 (step a) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Remove the library-wide -Wdocumentation pragma; fix what it hid macro_scope.hpp / macro_unscope.hpp pushed and popped a Clang diagnostic region over the entire library that ignored -Wdocumentation and -Wdocumentation-unknown-command. Removed both pragmas and fixed every finding a full -Wdocumentation (which implies -Wdocumentation-unknown-command and -Wdocumentation-deprecated-sync) build reports, so the library now compiles clean under Clang's documentation checks without a blanket suppression. Overlaps #5267, which is still open and edits a nearby doc block (json.hpp's get()/get_impl() @return, already fixed in the item 2 step (b) commit of this branch); this commit does not touch that block again. Unknown Doxygen alias commands (Doxyfile removed in #3071, so these were never rendered by anything) rewritten as plain prose, keeping the same information: - @requirement REQ-JSON-01 / REQ-JSON-02 (iter_impl.hpp, json_reverse_iterator.hpp): now "This class satisfies the following concept requirements (REQ-JSON-0N):". - @liveexample{prose,example-id} (three sites in json.hpp): kept the prose, dropped the command wrapper and the trailing example-id (docs/mkdocs/docs/examples/*.cpp still exist and are used directly by the rendered docs, not through this in-header alias) and unescaped the "\," commas that were only needed for the old alias's comma-separated argument syntax. - @complexity X (json.hpp x4, json_pointer.hpp x2, serializer.hpp x1): now "Complexity: X". Backslash sequences Clang's comment lexer tried to parse as commands, escaped to render as literal backslashes: - lexer.hpp get_codepoint(): two `\u` occurrences. - binary_reader.hpp get_bson_cstr() / get_bson_cstr_bulk(): two `\x00` occurrences. - serializer.hpp: three `\uXXXX` occurrences (constructor @param, append_codepoint_to_string_buffer() @brief, and the ensure_ascii member comment). One finding remained after all of the above: Clang reports "declaration is marked with '@deprecated' command but does not have a deprecation attribute" on the deprecated sax_parse(span_input_adapter&&, ...) overload, even though JSON_HEDLEY_DEPRECATED_FOR does expand to __attribute__((deprecated(...))) for Clang. Several isolated reproductions of this exact declaration shape - doc comment, template<>, two stacked __attribute__ macros, an overload set sharing the name - did not reproduce the warning, so this looks like a Clang comment/declaration-association quirk specific to this overload inside the much larger basic_json class template, not an actual documentation defect. Rather than keep the pragma library-wide for one Clang false positive, added a tightly scoped -Wdocumentation-deprecated-sync push/pop around just that overload. Verified with Apple clang 21 and the project's actual -Weverything flag set (cmake/clang_flags.cmake) on the full header at -std=c++11 and -std=c++20: zero -Wdocumentation* diagnostics. Also compiled clean with GCC 16.2.0 (the pragmas are already __clang__-gated, so this only confirms no unrelated breakage). Ran make check-amalgamation and the full local test suite: 129/129 passing. #5725 item 2 (step c) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Take the JSON value by const reference in the array and tuple from_json paths Review feedback on #5737 (gregmarr): once the no-op std::forward calls are gone, the forwarding references have no purpose. from_json_fn passes the value as const BasicJsonType&, so these functions were only ever instantiated with a const lvalue anyway. The std::array, std::pair and std::tuple overloads of from_json and their helpers now take const BasicJsonType& and pass j on unchanged. Because the deduced BasicJsonType is now the plain type, tuple_type and the static_assert name const BasicJsonType& explicitly, so the reference checks are unchanged: get<std::tuple<const std::string&>>() still works, and get<std::tuple<std::string&>>() still fails the same static_assert. from_json_tuple_get_impl keeps its forwarding reference, since tuple_type calls it through std::declval. Behavior, the public API and the ABI do not change. unit-conversions, unit-constructor1, unit-udt, unit-udt_macro, unit-regression1/2/3, unit-deserialization, unit-noexcept, unit-items, unit-allocator, unit-custom-object-type, unit-ordered_json2 and unit-brace-init-copy-semantics pass at C++11, C++17 and C++20 with unchanged assertion counts. Ran make amalgamate. 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) - false positive
|
|
NLOHMANN_JSON_SERIALIZE_ENUM(cards,
|
|
{
|
|
{cards::kreuz, "kreuz"},
|
|
{cards::pik, "pik"},
|
|
{cards::pik, "puk"}, // second entry for cards::puk; will not be used
|
|
{cards::herz, "herz"},
|
|
{cards::karo, "karo"}
|
|
})
|
|
|
|
enum TaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
|
{
|
|
TS_STOPPED,
|
|
TS_RUNNING,
|
|
TS_COMPLETED,
|
|
TS_INVALID = -1,
|
|
};
|
|
|
|
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
|
NLOHMANN_JSON_SERIALIZE_ENUM(TaskState,
|
|
{
|
|
{TS_INVALID, nullptr},
|
|
{TS_STOPPED, "stopped"},
|
|
{TS_RUNNING, "running"},
|
|
{TS_COMPLETED, "completed"},
|
|
})
|
|
|
|
TEST_CASE("JSON to enum mapping")
|
|
{
|
|
SECTION("enum class")
|
|
{
|
|
// enum -> json
|
|
CHECK(json(cards::kreuz) == "kreuz");
|
|
CHECK(json(cards::pik) == "pik");
|
|
CHECK(json(cards::herz) == "herz");
|
|
CHECK(json(cards::karo) == "karo");
|
|
|
|
// json -> enum
|
|
CHECK(cards::kreuz == json("kreuz"));
|
|
CHECK(cards::pik == json("pik"));
|
|
CHECK(cards::herz == json("herz"));
|
|
CHECK(cards::karo == json("karo"));
|
|
|
|
// invalid json -> first enum
|
|
CHECK(cards::kreuz == json("what?").get<cards>());
|
|
}
|
|
|
|
SECTION("traditional enum")
|
|
{
|
|
// enum -> json
|
|
CHECK(json(TS_STOPPED) == "stopped");
|
|
CHECK(json(TS_RUNNING) == "running");
|
|
CHECK(json(TS_COMPLETED) == "completed");
|
|
CHECK(json(TS_INVALID) == json());
|
|
|
|
// json -> enum
|
|
CHECK(TS_STOPPED == json("stopped"));
|
|
CHECK(TS_RUNNING == json("running"));
|
|
CHECK(TS_COMPLETED == json("completed"));
|
|
CHECK(TS_INVALID == json());
|
|
|
|
// invalid json -> first enum
|
|
CHECK(TS_INVALID == json("what?").get<TaskState>());
|
|
}
|
|
}
|
|
|
|
enum class strict_cards {kreuz, pik, herz, karo, andere}; // andere not included in mapping
|
|
|
|
// NOLINTNEXTLINE(misc-use-internal-linkage,misc-const-correctness) - false positive
|
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(strict_cards,
|
|
{
|
|
{strict_cards::kreuz, "kreuz"},
|
|
{strict_cards::pik, "pik"},
|
|
{strict_cards::pik, "puk"}, // second entry for cards::pik; will not be used
|
|
{strict_cards::herz, "herz"},
|
|
{strict_cards::karo, "karo"}
|
|
})
|
|
|
|
enum StrictTaskState // NOLINT(cert-int09-c,readability-enum-initial-value,cppcoreguidelines-use-enum-class)
|
|
{
|
|
STRICT_TS_STOPPED,
|
|
STRICT_TS_RUNNING,
|
|
STRICT_TS_COMPLETED,
|
|
STRICT_TS_OTHER, // STRICT_TS_OTHER not in mapping
|
|
STRICT_TS_INVALID = -1,
|
|
};
|
|
|
|
// NOLINTNEXTLINE(misc-const-correctness,misc-use-internal-linkage) - false positive
|
|
NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(StrictTaskState,
|
|
{
|
|
{STRICT_TS_INVALID, nullptr},
|
|
{STRICT_TS_STOPPED, "stopped"},
|
|
{STRICT_TS_RUNNING, "running"},
|
|
{STRICT_TS_COMPLETED, "completed"},
|
|
})
|
|
|
|
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
|