mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 13:10:33 +00: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>
1545 lines
56 KiB
C++
1545 lines
56 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
|
|
|
|
#include "doctest_compatibility.h"
|
|
|
|
// for some reason including this after the json header leads to linker errors with VS 2017...
|
|
#include <locale>
|
|
|
|
// 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;
|
|
#ifdef JSON_TEST_NO_GLOBAL_UDLS
|
|
using namespace nlohmann::literals; // NOLINT(google-build-using-namespace)
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <list>
|
|
#include <limits>
|
|
#include <cstdio>
|
|
#include "make_test_data_available.hpp"
|
|
#include "test_utils.hpp"
|
|
|
|
#include "fifo_map.hpp"
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// for #972
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
template<class K, class V, class dummy_compare, class A>
|
|
using my_workaround_fifo_map = nlohmann::fifo_map<K, V, nlohmann::fifo_map_compare<K>, A>;
|
|
using my_json = nlohmann::basic_json<my_workaround_fifo_map>;
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// for #977
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
namespace ns
|
|
{
|
|
struct foo
|
|
{
|
|
int x;
|
|
};
|
|
|
|
template <typename, typename SFINAE = void>
|
|
struct foo_serializer;
|
|
|
|
template<typename T>
|
|
struct foo_serializer<T, typename std::enable_if<std::is_same<foo, T>::value>::type>
|
|
{
|
|
template <typename BasicJsonType>
|
|
static void to_json(BasicJsonType& j, const T& value)
|
|
{
|
|
j = BasicJsonType{{"x", value.x}};
|
|
}
|
|
template <typename BasicJsonType>
|
|
static void from_json(const BasicJsonType& j, T& value) // !!!
|
|
{
|
|
nlohmann::from_json(j.at("x"), value.x);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
struct foo_serializer < T, typename std::enable_if < !std::is_same<foo, T>::value >::type >
|
|
{
|
|
template <typename BasicJsonType>
|
|
static void to_json(BasicJsonType& j, const T& value) noexcept // NOLINT(bugprone-exception-escape)
|
|
{
|
|
::nlohmann::to_json(j, value);
|
|
}
|
|
template <typename BasicJsonType>
|
|
static void from_json(const BasicJsonType& j, T& value) //!!!
|
|
{
|
|
::nlohmann::from_json(j, value);
|
|
}
|
|
};
|
|
} // namespace ns
|
|
|
|
using foo_json = nlohmann::basic_json<std::map, std::vector, std::string, bool, std::int64_t,
|
|
std::uint64_t, double, std::allocator, ns::foo_serializer, std::vector<std::uint8_t>>;
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// for #805
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
namespace
|
|
{
|
|
struct nocopy // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
|
|
{
|
|
nocopy() = default;
|
|
nocopy(const nocopy&) = delete;
|
|
nocopy(nocopy&&) = delete;
|
|
nocopy& operator=(const nocopy&) = delete;
|
|
nocopy& operator=(nocopy&&) = delete;
|
|
|
|
int val = 0;
|
|
|
|
friend void to_json(json& j, const nocopy& n)
|
|
{
|
|
j = {{"val", n.val}};
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
TEST_CASE("regression tests 1")
|
|
{
|
|
SECTION("issue #60 - Double quotation mark is not parsed correctly")
|
|
{
|
|
SECTION("escape_doublequote")
|
|
{
|
|
const auto* s = R"(["\"foo\""])";
|
|
const json j = json::parse(s);
|
|
auto expected = R"(["\"foo\""])"_json;
|
|
CHECK(j == expected);
|
|
}
|
|
}
|
|
|
|
SECTION("issue #70 - Handle infinity and NaN cases")
|
|
{
|
|
// previously, NAN/INFINITY created a null value; now, the values are
|
|
// properly stored, but are dumped as "null"
|
|
SECTION("NAN value")
|
|
{
|
|
CHECK(json(NAN).dump() == "null");
|
|
CHECK(json(json::number_float_t(NAN)).dump() == "null");
|
|
}
|
|
|
|
SECTION("infinity")
|
|
{
|
|
CHECK(json(INFINITY).dump() == "null");
|
|
CHECK(json(json::number_float_t(INFINITY)).dump() == "null");
|
|
}
|
|
|
|
// With 3.0.0, the semantics of this changed: NAN and infinity are
|
|
// stored properly inside the JSON value (no exception or conversion
|
|
// to null), but are serialized as null.
|
|
SECTION("NAN value")
|
|
{
|
|
json const j1 = NAN;
|
|
CHECK(j1.is_number_float());
|
|
json::number_float_t const f1{j1};
|
|
CHECK(std::isnan(f1));
|
|
|
|
json const j2 = static_cast<json::number_float_t>(NAN);
|
|
CHECK(j2.is_number_float());
|
|
json::number_float_t const f2{j2};
|
|
CHECK(std::isnan(f2));
|
|
}
|
|
|
|
SECTION("infinity")
|
|
{
|
|
json const j1 = INFINITY;
|
|
CHECK(j1.is_number_float());
|
|
json::number_float_t const f1{j1};
|
|
CHECK(!std::isfinite(f1));
|
|
|
|
json const j2 = static_cast<json::number_float_t>(INFINITY);
|
|
CHECK(j2.is_number_float());
|
|
json::number_float_t const f2{j2};
|
|
CHECK(!std::isfinite(f2));
|
|
}
|
|
}
|
|
|
|
#ifndef SKIP_TESTS_FOR_ENUM_SERIALIZATION
|
|
SECTION("pull request #71 - handle enum type")
|
|
{
|
|
enum { t = 0, u = 102}; // NOLINT(cppcoreguidelines-use-enum-class)
|
|
json j = json::array();
|
|
j.push_back(t);
|
|
|
|
// maybe this is not the place to test this?
|
|
const json j2 = u;
|
|
|
|
auto anon_enum_value = j2.get<decltype(u)>();
|
|
CHECK(u == anon_enum_value);
|
|
|
|
// check if the actual value was stored
|
|
CHECK(j2 == 102);
|
|
|
|
static_assert(std::is_same<decltype(anon_enum_value), decltype(u)>::value, "types must be the same");
|
|
|
|
j.push_back(json::object(
|
|
{
|
|
{"game_type", t}
|
|
}));
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #76 - dump() / parse() not idempotent")
|
|
{
|
|
// create JSON object
|
|
json fields;
|
|
fields["one"] = std::string("one");
|
|
fields["two"] = std::string("two three");
|
|
fields["three"] = std::string("three \"four\"");
|
|
|
|
// create another JSON object by deserializing the serialization
|
|
std::string const payload = fields.dump();
|
|
json parsed_fields = json::parse(payload);
|
|
|
|
// check individual fields to match both objects
|
|
CHECK(parsed_fields["one"] == fields["one"]);
|
|
CHECK(parsed_fields["two"] == fields["two"]);
|
|
CHECK(parsed_fields["three"] == fields["three"]);
|
|
|
|
// check individual fields to match original input
|
|
CHECK(parsed_fields["one"] == std::string("one"));
|
|
CHECK(parsed_fields["two"] == std::string("two three"));
|
|
CHECK(parsed_fields["three"] == std::string("three \"four\""));
|
|
|
|
// check equality of the objects
|
|
CHECK(parsed_fields == fields);
|
|
|
|
// check equality of the serialized objects
|
|
CHECK(fields.dump() == parsed_fields.dump());
|
|
|
|
// check everything in one line
|
|
CHECK(fields == json::parse(fields.dump()));
|
|
}
|
|
|
|
SECTION("issue #82 - lexer::get_number return NAN")
|
|
{
|
|
const auto* const content = R"(
|
|
{
|
|
"Test":"Test1",
|
|
"Number":100,
|
|
"Foo":42.42
|
|
})";
|
|
|
|
std::stringstream ss;
|
|
ss << content;
|
|
json j;
|
|
ss >> j;
|
|
|
|
auto test = j["Test"].get<std::string>();
|
|
CHECK(test == "Test1");
|
|
const int number{j["Number"]};
|
|
CHECK(number == 100);
|
|
const float foo{j["Foo"]};
|
|
CHECK(static_cast<double>(foo) == Approx(42.42));
|
|
}
|
|
|
|
SECTION("issue #89 - nonstandard integer type")
|
|
{
|
|
// create JSON class with nonstandard integer number type
|
|
using custom_json =
|
|
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float>;
|
|
custom_json j;
|
|
j["int_1"] = 1;
|
|
CHECK(j["int_1"] == 1);
|
|
|
|
// tests for correct handling of non-standard integers that overflow the type selected by the user
|
|
|
|
// unsigned integer object creation - expected to wrap and still be stored as an integer
|
|
j = 4294967296U; // 2^32
|
|
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_unsigned));
|
|
CHECK(j.get<uint32_t>() == 0); // Wrap
|
|
|
|
// unsigned integer parsing - expected to overflow and be stored as a float
|
|
j = custom_json::parse("4294967296"); // 2^32
|
|
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_float));
|
|
CHECK(j.get<float>() == 4294967296.0f);
|
|
|
|
// integer object creation - expected to wrap and still be stored as an integer
|
|
j = -2147483649LL; // -2^31-1
|
|
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_integer));
|
|
CHECK(j.get<int32_t>() == 2147483647); // Wrap
|
|
|
|
// integer parsing - expected to overflow and be stored as a float with rounding
|
|
j = custom_json::parse("-2147483649"); // -2^31
|
|
CHECK(static_cast<int>(j.type()) == static_cast<int>(custom_json::value_t::number_float));
|
|
CHECK(j.get<float>() == -2147483650.0f);
|
|
}
|
|
|
|
SECTION("issue #93 reverse_iterator operator inheritance problem")
|
|
{
|
|
{
|
|
json a = {1, 2, 3};
|
|
json::reverse_iterator rit = a.rbegin();
|
|
++rit;
|
|
CHECK(*rit == json(2));
|
|
CHECK(rit.value() == json(2));
|
|
}
|
|
{
|
|
json a = {1, 2, 3};
|
|
json::reverse_iterator const rit = ++a.rbegin();
|
|
CHECK(*rit == json(2));
|
|
CHECK(rit.value() == json(2));
|
|
}
|
|
{
|
|
json a = {1, 2, 3};
|
|
json::reverse_iterator rit = a.rbegin();
|
|
++rit;
|
|
json b = {0, 0, 0};
|
|
std::transform(rit, a.rend(), b.rbegin(), [](json el)
|
|
{
|
|
return el;
|
|
});
|
|
CHECK(b == json({0, 1, 2}));
|
|
}
|
|
{
|
|
json a = {1, 2, 3};
|
|
json b = {0, 0, 0};
|
|
std::transform(++a.rbegin(), a.rend(), b.rbegin(), [](json el)
|
|
{
|
|
return el;
|
|
});
|
|
CHECK(b == json({0, 1, 2}));
|
|
}
|
|
}
|
|
|
|
SECTION("issue #100 - failed to iterator json object with reverse_iterator")
|
|
{
|
|
json config =
|
|
{
|
|
{ "111", 111 },
|
|
{ "112", 112 },
|
|
{ "113", 113 }
|
|
};
|
|
|
|
std::stringstream ss;
|
|
|
|
for (auto it = config.begin(); it != config.end(); ++it)
|
|
{
|
|
ss << it.key() << ": " << it.value() << '\n';
|
|
}
|
|
|
|
for (auto it = config.rbegin(); it != config.rend(); ++it)
|
|
{
|
|
ss << it.key() << ": " << it.value() << '\n';
|
|
}
|
|
|
|
CHECK(ss.str() == "111: 111\n112: 112\n113: 113\n113: 113\n112: 112\n111: 111\n");
|
|
}
|
|
|
|
SECTION("issue #101 - binary string causes numbers to be dumped as hex")
|
|
{
|
|
int64_t const number = 10;
|
|
std::string const bytes{"\x00" "asdf\n", 6};
|
|
json j;
|
|
j["int64"] = number;
|
|
j["binary string"] = bytes;
|
|
// make sure the number is really printed as decimal "10" and not as
|
|
// hexadecimal "a"
|
|
CHECK(j.dump() == "{\"binary string\":\"\\u0000asdf\\n\",\"int64\":10}");
|
|
}
|
|
|
|
SECTION("issue #111 - subsequent unicode chars")
|
|
{
|
|
std::string const bytes{0x7, 0x7};
|
|
json j;
|
|
j["string"] = bytes;
|
|
CHECK(j["string"] == "\u0007\u0007");
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #144 - implicit assignment to std::string fails")
|
|
{
|
|
json o = {{"name", "value"}};
|
|
|
|
const std::string s1 = o["name"];
|
|
CHECK(s1 == "value");
|
|
|
|
std::string s2;
|
|
s2 = o["name"];
|
|
|
|
CHECK(s2 == "value");
|
|
|
|
// improve coverage
|
|
o["int"] = 1;
|
|
#if JSON_DIAGNOSTICS
|
|
CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number", json::type_error);
|
|
#else
|
|
CHECK_THROWS_WITH_AS(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number", json::type_error);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #146 - character following a surrogate pair is skipped")
|
|
{
|
|
CHECK(json::parse("\"\\ud80c\\udc60abc\"").get<json::string_t>() == "\xf0\x93\x81\xa0\x61\x62\x63");
|
|
}
|
|
|
|
SECTION("issue #171 - Cannot index by key of type static constexpr const char*")
|
|
{
|
|
json j;
|
|
|
|
// Non-const access with key as "char []"
|
|
char array_key[] = "Key1"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
CHECK_NOTHROW(j[array_key] = 1);
|
|
CHECK(j[array_key] == json(1));
|
|
|
|
// Non-const access with key as "const char[]"
|
|
const char const_array_key[] = "Key2"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
CHECK_NOTHROW(j[const_array_key] = 2);
|
|
CHECK(j[const_array_key] == json(2));
|
|
|
|
// Non-const access with key as "char *"
|
|
char _ptr_key[] = "Key3"; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
char* ptr_key = &_ptr_key[0]; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg)
|
|
CHECK_NOTHROW(j[ptr_key] = 3);
|
|
CHECK(j[ptr_key] == json(3));
|
|
|
|
// Non-const access with key as "const char *"
|
|
const char* const_ptr_key = "Key4";
|
|
CHECK_NOTHROW(j[const_ptr_key] = 4);
|
|
CHECK(j[const_ptr_key] == json(4));
|
|
|
|
// Non-const access with key as "static constexpr const char *"
|
|
static constexpr const char* constexpr_ptr_key = "Key5";
|
|
CHECK_NOTHROW(j[constexpr_ptr_key] = 5);
|
|
CHECK(j[constexpr_ptr_key] == json(5));
|
|
|
|
const json j_const = j;
|
|
|
|
// Const access with key as "char []"
|
|
CHECK(j_const[array_key] == json(1));
|
|
|
|
// Const access with key as "const char[]"
|
|
CHECK(j_const[const_array_key] == json(2));
|
|
|
|
// Const access with key as "char *"
|
|
CHECK(j_const[ptr_key] == json(3));
|
|
|
|
// Const access with key as "const char *"
|
|
CHECK(j_const[const_ptr_key] == json(4));
|
|
|
|
// Const access with key as "static constexpr const char *"
|
|
CHECK(j_const[constexpr_ptr_key] == json(5));
|
|
}
|
|
|
|
SECTION("issue #186 miloyip/nativejson-benchmark: floating-point parsing")
|
|
{
|
|
json j;
|
|
|
|
j = json::parse("-0.0");
|
|
CHECK(j.get<double>() == -0.0);
|
|
|
|
j = json::parse("2.22507385850720113605740979670913197593481954635164564e-308");
|
|
CHECK(j.get<double>() == 2.2250738585072009e-308);
|
|
|
|
j = json::parse("0.999999999999999944488848768742172978818416595458984374");
|
|
CHECK(j.get<double>() == 0.99999999999999989);
|
|
|
|
j = json::parse("1.00000000000000011102230246251565404236316680908203126");
|
|
CHECK(j.get<double>() == 1.00000000000000022);
|
|
|
|
j = json::parse("7205759403792793199999e-5");
|
|
CHECK(j.get<double>() == 72057594037927928.0);
|
|
|
|
j = json::parse("922337203685477529599999e-5");
|
|
CHECK(j.get<double>() == 9223372036854774784.0);
|
|
|
|
j = json::parse("1014120480182583464902367222169599999e-5");
|
|
CHECK(j.get<double>() == 10141204801825834086073718800384.0);
|
|
|
|
j = json::parse("5708990770823839207320493820740630171355185151999e-3");
|
|
CHECK(j.get<double>() == 5708990770823838890407843763683279797179383808.0);
|
|
|
|
// create JSON class with nonstandard float number type
|
|
|
|
// float
|
|
nlohmann::basic_json<std::map, std::vector, std::string, bool, int32_t, uint32_t, float> const j_float =
|
|
1.23e25f;
|
|
CHECK(j_float.get<float>() == 1.23e25f);
|
|
|
|
// double
|
|
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, double> const j_double =
|
|
1.23e35;
|
|
CHECK(j_double.get<double>() == 1.23e35);
|
|
|
|
// long double
|
|
nlohmann::basic_json<std::map, std::vector, std::string, bool, int64_t, uint64_t, long double>
|
|
const j_long_double = 1.23e45L;
|
|
CHECK(j_long_double.get<long double>() == 1.23e45L);
|
|
}
|
|
|
|
SECTION("issue #228 - double values are serialized with commas as decimal points")
|
|
{
|
|
json const j1a = 2312.42;
|
|
json const j1b = json::parse("2312.42");
|
|
|
|
json const j2a = 2342e-2;
|
|
//issue #230
|
|
//json j2b = json::parse("2342e-2");
|
|
|
|
json const j3a = 10E3;
|
|
json const j3b = json::parse("10E3");
|
|
json const j3c = json::parse("10e3");
|
|
|
|
// class to create a locale that would use a comma for decimals
|
|
class CommaDecimalSeparator : public std::numpunct<char>
|
|
{
|
|
protected:
|
|
char do_decimal_point() const override
|
|
{
|
|
return ',';
|
|
}
|
|
|
|
char do_thousands_sep() const override
|
|
{
|
|
return '.';
|
|
}
|
|
|
|
std::string do_grouping() const override
|
|
{
|
|
return "\03";
|
|
}
|
|
};
|
|
|
|
// change locale to mess with decimal points
|
|
auto orig_locale = std::locale::global(std::locale(std::locale(), new CommaDecimalSeparator));
|
|
|
|
CHECK(j1a.dump() == "2312.42");
|
|
CHECK(j1b.dump() == "2312.42");
|
|
|
|
// check if locale is properly reset
|
|
std::stringstream ss;
|
|
ss.imbue(std::locale(std::locale(), new CommaDecimalSeparator));
|
|
ss << 4712.11;
|
|
CHECK(ss.str() == "4.712,11");
|
|
ss << j1a;
|
|
CHECK(ss.str() == "4.712,112312.42");
|
|
ss << 47.11;
|
|
CHECK(ss.str() == "4.712,112312.4247,11");
|
|
|
|
CHECK(j2a.dump() == "23.42");
|
|
//issue #230
|
|
//CHECK(j2b.dump() == "23.42");
|
|
|
|
CHECK(j3a.dump() == "10000.0");
|
|
CHECK(j3b.dump() == "10000.0");
|
|
CHECK(j3c.dump() == "10000.0");
|
|
//CHECK(j3b.dump() == "1E04"); // roundtrip error
|
|
//CHECK(j3c.dump() == "1e04"); // roundtrip error
|
|
|
|
std::locale::global(orig_locale);
|
|
}
|
|
|
|
SECTION("issue #378 - locale-independent num-to-str")
|
|
{
|
|
static_cast<void>(setlocale(LC_NUMERIC, "de_DE.UTF-8"));
|
|
|
|
// verify that dumped correctly with '.' and no grouping
|
|
const json j1 = 12345.67;
|
|
CHECK(json(12345.67).dump() == "12345.67");
|
|
static_cast<void>(setlocale(LC_NUMERIC, "C"));
|
|
}
|
|
|
|
SECTION("issue #379 - locale-independent str-to-num")
|
|
{
|
|
static_cast<void>(setlocale(LC_NUMERIC, "de_DE.UTF-8"));
|
|
|
|
// verify that parsed correctly despite using strtod internally
|
|
CHECK(json::parse("3.14").get<double>() == 3.14);
|
|
|
|
// check a different code path
|
|
CHECK(json::parse("1.000000000000000000000000000000000000000000000000000000000000000000000000").get<double>() == 1.0);
|
|
}
|
|
|
|
SECTION("issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator")
|
|
{
|
|
json source = {"a", "b", "c"};
|
|
json expected = {"a", "b"};
|
|
json dest;
|
|
|
|
std::copy_n(std::make_move_iterator(source.begin()), 2, std::back_inserter(dest));
|
|
|
|
CHECK(dest == expected);
|
|
}
|
|
|
|
SECTION("issue #235 - ambiguous overload for 'push_back' and 'operator+='")
|
|
{
|
|
json data = {{"key", "value"}};
|
|
data.push_back({"key2", "value2"});
|
|
data += {"key3", "value3"};
|
|
|
|
CHECK(data == json({{"key", "value"}, {"key2", "value2"}, {"key3", "value3"}}));
|
|
}
|
|
|
|
SECTION("issue #269 - diff generates incorrect patch when removing multiple array elements")
|
|
{
|
|
json const doc = R"( { "arr1": [1, 2, 3, 4] } )"_json;
|
|
json expected = R"( { "arr1": [1, 2] } )"_json;
|
|
|
|
// check roundtrip
|
|
CHECK(doc.patch(json::diff(doc, expected)) == expected);
|
|
}
|
|
|
|
SECTION("issue #283 - value() does not work with _json_pointer types")
|
|
{
|
|
json j =
|
|
{
|
|
{"object", {{"key1", 1}, {"key2", 2}}},
|
|
};
|
|
|
|
const int at_integer{j.at("/object/key2"_json_pointer)};
|
|
const int val_integer = j.value("/object/key2"_json_pointer, 0);
|
|
|
|
CHECK(at_integer == val_integer);
|
|
}
|
|
|
|
SECTION("issue #304 - Unused variable warning")
|
|
{
|
|
// code triggered a "warning: unused variable" warning and is left
|
|
// here to avoid the warning in the future
|
|
json object;
|
|
json const patch = json::array();
|
|
object = object.patch(patch);
|
|
}
|
|
|
|
SECTION("issue #306 - Parsing fails without space at end of file")
|
|
{
|
|
for (const auto* filename :
|
|
{
|
|
TEST_DATA_DIRECTORY "/regression/broken_file.json",
|
|
TEST_DATA_DIRECTORY "/regression/working_file.json"
|
|
})
|
|
{
|
|
CAPTURE(filename)
|
|
json j;
|
|
std::ifstream f(filename);
|
|
CHECK_NOTHROW(f >> j);
|
|
}
|
|
}
|
|
|
|
SECTION("issue #310 - make json_benchmarks no longer working in 2.0.4")
|
|
{
|
|
for (const auto* filename :
|
|
{
|
|
TEST_DATA_DIRECTORY "/regression/floats.json",
|
|
TEST_DATA_DIRECTORY "/regression/signed_ints.json",
|
|
TEST_DATA_DIRECTORY "/regression/unsigned_ints.json",
|
|
TEST_DATA_DIRECTORY "/regression/small_signed_ints.json"
|
|
})
|
|
{
|
|
CAPTURE(filename)
|
|
json j;
|
|
std::ifstream f(filename);
|
|
CHECK_NOTHROW(f >> j);
|
|
}
|
|
}
|
|
|
|
SECTION("issue #323 - add nested object capabilities to pointers")
|
|
{
|
|
json j;
|
|
j["/this/that/2"_json_pointer] = 27;
|
|
CHECK(j == json({{"this", {{"that", {nullptr, nullptr, 27}}}}}));
|
|
}
|
|
|
|
SECTION("issue #329 - serialized value not always can be parsed")
|
|
{
|
|
json _;
|
|
CHECK_THROWS_WITH_AS(_ = json::parse("22e2222"), "[json.exception.out_of_range.406] number overflow parsing '22e2222'", json::out_of_range&);
|
|
}
|
|
|
|
SECTION("issue #360 - Loss of precision when serializing <double>")
|
|
{
|
|
auto check_roundtrip = [](double number)
|
|
{
|
|
CAPTURE(number)
|
|
|
|
json j = number;
|
|
CHECK(j.is_number_float());
|
|
|
|
std::stringstream ss;
|
|
ss << j;
|
|
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j.is_number_float());
|
|
CHECK(j.get<json::number_float_t>() == number);
|
|
};
|
|
|
|
check_roundtrip(100000000000.1236);
|
|
check_roundtrip((std::numeric_limits<json::number_float_t>::max)());
|
|
|
|
// Some more numbers which fail to roundtrip when serialized with digits10 significand digits (instead of max_digits10)
|
|
check_roundtrip(1.541888611948064e-17);
|
|
check_roundtrip(5.418771028591015e-16);
|
|
check_roundtrip(9.398685592608595e-15);
|
|
check_roundtrip(8.826843952762347e-14);
|
|
check_roundtrip(8.143291313475335e-13);
|
|
check_roundtrip(4.851328172762508e-12);
|
|
check_roundtrip(6.677850998084358e-11);
|
|
check_roundtrip(3.995398518174525e-10);
|
|
check_roundtrip(1.960452605645124e-9);
|
|
check_roundtrip(3.551812586302883e-8);
|
|
check_roundtrip(2.947988411689261e-7);
|
|
check_roundtrip(8.210166748056192e-6);
|
|
check_roundtrip(6.104889704266753e-5);
|
|
check_roundtrip(0.0008629954631330876);
|
|
check_roundtrip(0.004936993881051611);
|
|
check_roundtrip(0.08309725102608073);
|
|
check_roundtrip(0.5210494268499783);
|
|
check_roundtrip(6.382927930939767);
|
|
check_roundtrip(59.94947245358671);
|
|
check_roundtrip(361.0838651266122);
|
|
check_roundtrip(4678.354596181877);
|
|
check_roundtrip(61412.17658956043);
|
|
check_roundtrip(725696.0799057782);
|
|
check_roundtrip(2811732.583399828);
|
|
check_roundtrip(30178351.07533605);
|
|
check_roundtrip(689684880.3235844);
|
|
check_roundtrip(5714887673.555147);
|
|
check_roundtrip(84652038821.18808);
|
|
check_roundtrip(156510583431.7721);
|
|
check_roundtrip(5938450569021.732);
|
|
check_roundtrip(83623297654460.33);
|
|
check_roundtrip(701466573254773.6);
|
|
check_roundtrip(1369013370304513);
|
|
check_roundtrip(96963648023094720); // NOLINT(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
|
|
check_roundtrip(3.478237409280108e+17);
|
|
}
|
|
|
|
SECTION("issue #366 - json::parse on failed stream gets stuck")
|
|
{
|
|
std::ifstream f("file_not_found.json");
|
|
json _;
|
|
CHECK_THROWS_WITH_AS(_ = json::parse(f), "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #367 - calling stream at EOF")
|
|
{
|
|
std::stringstream ss;
|
|
json j;
|
|
ss << "123";
|
|
CHECK_NOTHROW(ss >> j);
|
|
|
|
// see https://github.com/nlohmann/json/issues/367#issuecomment-262841893:
|
|
// ss is not at EOF; this yielded an error before the fix
|
|
// (threw basic_string::append). No, it should just throw
|
|
// a parse error because of the EOF.
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #367 - behavior of operator>> should more closely resemble that of built-in overloads")
|
|
{
|
|
SECTION("(empty)")
|
|
{
|
|
std::stringstream ss;
|
|
json j;
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("(whitespace)")
|
|
{
|
|
std::stringstream ss;
|
|
ss << " ";
|
|
json j;
|
|
CHECK_THROWS_WITH_AS(ss >> j,
|
|
"[json.exception.parse_error.101] parse error at line 1, column 4: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
|
|
}
|
|
|
|
SECTION("one value")
|
|
{
|
|
std::stringstream ss;
|
|
ss << "111";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 111);
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("one value + whitespace")
|
|
{
|
|
std::stringstream ss;
|
|
ss << "222 \t\n";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 222);
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j,
|
|
"[json.exception.parse_error.101] parse error at line 2, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error&);
|
|
}
|
|
|
|
SECTION("whitespace + one value")
|
|
{
|
|
std::stringstream ss;
|
|
ss << "\n\t 333";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 333);
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("three values")
|
|
{
|
|
std::stringstream ss;
|
|
ss << " 111 \n222\n \n 333";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 111);
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 222);
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == 333);
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("literals without whitespace")
|
|
{
|
|
std::stringstream ss;
|
|
ss << "truefalsenull\"\"";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == true);
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == false);
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == nullptr);
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == "");
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("example from #529")
|
|
{
|
|
std::stringstream ss;
|
|
ss << "{\n \"one\" : 1,\n \"two\" : 2\n}\n{\n \"three\" : 3\n}";
|
|
json j;
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == json({{"one", 1}, {"two", 2}}));
|
|
CHECK_NOTHROW(ss >> j);
|
|
CHECK(j == json({{"three", 3}}));
|
|
|
|
CHECK_THROWS_WITH_AS(ss >> j, "[json.exception.parse_error.101] parse error at line 1, column 1: attempting to parse an empty input; check that your input string or stream contains the expected JSON", json::parse_error&);
|
|
}
|
|
|
|
SECTION("second example from #529")
|
|
{
|
|
std::string const str = "{\n\"one\" : 1,\n\"two\" : 2\n}\n{\n\"three\" : 3\n}";
|
|
|
|
{
|
|
std::ofstream file("test.json");
|
|
file << str;
|
|
}
|
|
|
|
std::ifstream stream("test.json", std::ifstream::in);
|
|
json val;
|
|
|
|
size_t i = 0;
|
|
while (stream.peek() != EOF)
|
|
{
|
|
CAPTURE(i)
|
|
CHECK_NOTHROW(stream >> val);
|
|
|
|
CHECK(i < 2);
|
|
|
|
if (i == 0)
|
|
{
|
|
CHECK(val == json({{"one", 1}, {"two", 2}}));
|
|
}
|
|
|
|
if (i == 1)
|
|
{
|
|
CHECK(val == json({{"three", 3}}));
|
|
}
|
|
|
|
++i;
|
|
}
|
|
|
|
static_cast<void>(std::remove("test.json"));
|
|
}
|
|
}
|
|
|
|
SECTION("issue #389 - Integer-overflow (OSS-Fuzz issue 267)")
|
|
{
|
|
// original test case
|
|
json const j1 = json::parse("-9223372036854775808");
|
|
CHECK(j1.is_number_integer());
|
|
CHECK(j1.get<json::number_integer_t>() == (std::numeric_limits<std::int64_t>::min)());
|
|
|
|
// edge case (+1; still an integer)
|
|
json const j2 = json::parse("-9223372036854775807");
|
|
CHECK(j2.is_number_integer());
|
|
CHECK(j2.get<json::number_integer_t>() == (std::numeric_limits<std::int64_t>::min)() + 1);
|
|
|
|
// edge case (-1; overflow -> floats)
|
|
json const j3 = json::parse("-9223372036854775809");
|
|
CHECK(j3.is_number_float());
|
|
}
|
|
|
|
SECTION("issue #380 - bug in overflow detection when parsing integers")
|
|
{
|
|
json const j = json::parse("166020696663385964490");
|
|
CHECK(j.is_number_float());
|
|
CHECK(j.get<json::number_float_t>() == 166020696663385964490.0);
|
|
}
|
|
|
|
SECTION("issue #405 - Heap-buffer-overflow (OSS-Fuzz issue 342)")
|
|
{
|
|
// original test case
|
|
std::vector<uint8_t> const vec {0x65, 0xf5, 0x0a, 0x48, 0x21};
|
|
json _;
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.110] parse error at byte 6: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #407 - Heap-buffer-overflow (OSS-Fuzz issue 343)")
|
|
{
|
|
json _;
|
|
|
|
// original test case: incomplete float64
|
|
std::vector<uint8_t> const vec1 {0xcb, 0x8f, 0x0a};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: incomplete float32
|
|
std::vector<uint8_t> const vec2 {0xca, 0x8f, 0x0a};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing MessagePack number: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: incomplete Half-Precision Float (CBOR)
|
|
std::vector<uint8_t> const vec3 {0xf9, 0x8f};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: incomplete Single-Precision Float (CBOR)
|
|
std::vector<uint8_t> const vec4 {0xfa, 0x8f, 0x0a};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec4), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: incomplete Double-Precision Float (CBOR)
|
|
std::vector<uint8_t> const vec5 {0xfb, 0x8f, 0x0a};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec5), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR number: unexpected end of input", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #408 - Heap-buffer-overflow (OSS-Fuzz issue 344)")
|
|
{
|
|
json _;
|
|
|
|
// original test case
|
|
std::vector<uint8_t> const vec1 {0x87};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing MessagePack string: unexpected end of input", json::parse_error&);
|
|
|
|
// more test cases for MessagePack
|
|
for (auto b :
|
|
{
|
|
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, // fixmap
|
|
0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, // fixarray
|
|
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, // fixstr
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf
|
|
})
|
|
{
|
|
std::vector<uint8_t> const vec(1, static_cast<uint8_t>(b));
|
|
CHECK_THROWS_AS(_ = json::from_msgpack(vec), json::parse_error&);
|
|
}
|
|
|
|
// more test cases for CBOR
|
|
for (auto b :
|
|
{
|
|
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
|
|
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // UTF-8 string
|
|
0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, // array
|
|
0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
|
|
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7 // map
|
|
})
|
|
{
|
|
std::vector<uint8_t> const vec(1, static_cast<uint8_t>(b));
|
|
CHECK_THROWS_AS(_ = json::from_cbor(vec), json::parse_error&);
|
|
}
|
|
|
|
// special case: empty input
|
|
std::vector<uint8_t> const vec2;
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
|
|
CHECK_THROWS_WITH_AS(_ = json::from_msgpack(vec2), "[json.exception.parse_error.110] parse error at byte 1: syntax error while parsing MessagePack value: unexpected end of input", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #411 - Heap-buffer-overflow (OSS-Fuzz issue 366)")
|
|
{
|
|
json _;
|
|
|
|
// original test case: empty UTF-8 string (indefinite length)
|
|
std::vector<uint8_t> const vec1 {0x7f};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: empty array (indefinite length)
|
|
std::vector<uint8_t> const vec2 {0x9f};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: empty map (indefinite length)
|
|
std::vector<uint8_t> const vec3 {0xbf};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 2: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #412 - Heap-buffer-overflow (OSS-Fuzz issue 367)")
|
|
{
|
|
// original test case
|
|
std::vector<uint8_t> const vec
|
|
{
|
|
0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
|
|
0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00,
|
|
0x60, 0xab, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
|
|
0x98, 0x98, 0x98, 0x98, 0x98, 0x00, 0x00, 0x00,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xa0, 0x9f,
|
|
0x9f, 0x97, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
|
|
0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60
|
|
};
|
|
|
|
json _;
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec), "[json.exception.parse_error.113] parse error at byte 2: syntax error while parsing CBOR object key: only string keys are supported, but found an array; last byte: 0x98", json::parse_error&);
|
|
|
|
// related test case: nonempty UTF-8 string (indefinite length)
|
|
std::vector<uint8_t> const vec1 {0x7f, 0x61, 0x61};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.110] parse error at byte 4: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: nonempty array (indefinite length)
|
|
std::vector<uint8_t> const vec2 {0x9f, 0x01};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.110] parse error at byte 3: syntax error while parsing CBOR value: unexpected end of input", json::parse_error&);
|
|
|
|
// related test case: nonempty map (indefinite length)
|
|
std::vector<uint8_t> const vec3 {0xbf, 0x61, 0x61, 0x01};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec3), "[json.exception.parse_error.110] parse error at byte 5: syntax error while parsing CBOR string: unexpected end of input", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #414 - compare with literal 0)")
|
|
{
|
|
#define CHECK_TYPE(v) \
|
|
CHECK((json(v) == (v)));\
|
|
CHECK(((v) == json(v)));\
|
|
CHECK_FALSE((json(v) != (v)));\
|
|
CHECK_FALSE(((v) != json(v)));
|
|
|
|
CHECK_TYPE(nullptr)
|
|
CHECK_TYPE(0)
|
|
CHECK_TYPE(0u)
|
|
CHECK_TYPE(0L)
|
|
CHECK_TYPE(0.0)
|
|
CHECK_TYPE("") // NOLINT(readability-container-size-empty)
|
|
|
|
#undef CHECK_TYPE
|
|
}
|
|
|
|
SECTION("issue #416 - Use-of-uninitialized-value (OSS-Fuzz issue 377)")
|
|
{
|
|
// original test case
|
|
std::vector<uint8_t> const vec1
|
|
{
|
|
0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
|
|
0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
|
|
0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71,
|
|
0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a,
|
|
0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
|
|
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfa
|
|
};
|
|
|
|
json _;
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec1), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR object key: only string keys are supported, but found a map; last byte: 0xB4", json::parse_error&);
|
|
|
|
// related test case: double-precision
|
|
std::vector<uint8_t> const vec2
|
|
{
|
|
0x94, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa,
|
|
0x3a, 0x96, 0x96, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4,
|
|
0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0xb4, 0x71,
|
|
0xb4, 0xb4, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0x3a,
|
|
0x96, 0x96, 0xb4, 0xb4, 0xfa, 0x94, 0x94, 0x61,
|
|
0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xfb
|
|
};
|
|
CHECK_THROWS_WITH_AS(_ = json::from_cbor(vec2), "[json.exception.parse_error.113] parse error at byte 13: syntax error while parsing CBOR object key: only string keys are supported, but found a map; last byte: 0xB4", json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #452 - Heap-buffer-overflow (OSS-Fuzz issue 585)")
|
|
{
|
|
std::vector<uint8_t> const vec = {'-', '0', '1', '2', '2', '7', '4'};
|
|
json _;
|
|
CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&);
|
|
}
|
|
|
|
SECTION("issue #454 - doubles are printed as integers")
|
|
{
|
|
json j = R"({"bool_value":true,"double_value":2.0,"int_value":10,"level1":{"list_value":[3,"hi",false],"tmp":5.0},"string_value":"hello"})"_json;
|
|
CHECK(j["double_value"].is_number_float());
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #464 - VS2017 implicit to std::string conversion fix")
|
|
{
|
|
const json v = "test";
|
|
std::string test;
|
|
test = v;
|
|
CHECK(v == "test");
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #465 - roundtrip error while parsing 1000000000000000010E5")
|
|
{
|
|
json const j1 = json::parse("1000000000000000010E5");
|
|
const std::string s1 = j1.dump();
|
|
json const j2 = json::parse(s1);
|
|
const std::string s2 = j2.dump();
|
|
CHECK(s1 == s2);
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #473 - inconsistent behavior in conversion to array type")
|
|
{
|
|
json const j_array = {1, 2, 3, 4};
|
|
json const j_number = 42;
|
|
json const j_null = nullptr;
|
|
|
|
SECTION("std::vector")
|
|
{
|
|
auto create = [](const json & j)
|
|
{
|
|
std::vector<int> const v = j;
|
|
};
|
|
|
|
CHECK_NOTHROW(create(j_array));
|
|
CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
}
|
|
|
|
SECTION("std::list")
|
|
{
|
|
auto create = [](const json & j)
|
|
{
|
|
std::list<int> const v = j;
|
|
};
|
|
|
|
CHECK_NOTHROW(create(j_array));
|
|
CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
}
|
|
|
|
SECTION("std::forward_list")
|
|
{
|
|
auto create = [](const json & j)
|
|
{
|
|
std::forward_list<int> const v = j;
|
|
};
|
|
|
|
CHECK_NOTHROW(create(j_array));
|
|
CHECK_THROWS_WITH_AS(create(j_number), "[json.exception.type_error.302] type must be array, but is number", json::type_error&);
|
|
CHECK_THROWS_WITH_AS(create(j_null), "[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #486 - json::value_t can't be a map's key type in VC++ 2015")
|
|
{
|
|
// the code below must compile with MSVC
|
|
std::map<json::value_t, std::string> jsonTypes ;
|
|
jsonTypes[json::value_t::array] = "array";
|
|
}
|
|
|
|
SECTION("issue #494 - conversion from vector<bool> to json fails to build")
|
|
{
|
|
std::vector<bool> const boolVector = {false, true, false, false};
|
|
json j;
|
|
j["bool_vector"] = boolVector;
|
|
|
|
CHECK(j["bool_vector"].dump() == "[false,true,false,false]");
|
|
}
|
|
|
|
SECTION("issue #504 - assertion error (OSS-Fuzz 856)")
|
|
{
|
|
std::vector<uint8_t> const vec1 = {0xf9, 0xff, 0xff, 0x4a, 0x3a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x37, 0x02, 0x38};
|
|
json const j1 = json::from_cbor(vec1, false);
|
|
|
|
// step 2: round trip
|
|
std::vector<uint8_t> vec2 = json::to_cbor(j1);
|
|
|
|
// parse serialization
|
|
json const j2 = json::from_cbor(vec2);
|
|
|
|
// NaN is dumped to "null"
|
|
CHECK(j2.is_number_float());
|
|
CHECK(std::isnan(j2.get<json::number_float_t>()));
|
|
CHECK(j2.dump() == "null");
|
|
|
|
// check if serializations match
|
|
CHECK(json::to_cbor(j2) == vec2);
|
|
}
|
|
|
|
SECTION("issue #512 - use of overloaded operator '<=' is ambiguous")
|
|
{
|
|
json j;
|
|
j["a"] = 5;
|
|
|
|
// json op scalar
|
|
CHECK(j["a"] == 5);
|
|
CHECK(j["a"] != 4);
|
|
|
|
CHECK(j["a"] <= 7);
|
|
CHECK(j["a"] < 7);
|
|
CHECK(j["a"] >= 3);
|
|
CHECK(j["a"] > 3);
|
|
|
|
CHECK(!(j["a"] <= 4));
|
|
CHECK(!(j["a"] < 4));
|
|
CHECK(!(j["a"] >= 6));
|
|
CHECK(!(j["a"] > 6));
|
|
|
|
// scalar op json
|
|
CHECK(5 == j["a"]);
|
|
CHECK(4 != j["a"]);
|
|
|
|
CHECK(7 >= j["a"]);
|
|
CHECK(7 > j["a"]);
|
|
CHECK(3 <= j["a"]);
|
|
CHECK(3 < j["a"]);
|
|
|
|
CHECK(!(4 >= j["a"]));
|
|
CHECK(!(4 > j["a"]));
|
|
CHECK(!(6 <= j["a"]));
|
|
CHECK(!(6 < j["a"]));
|
|
}
|
|
|
|
SECTION("issue #575 - heap-buffer-overflow (OSS-Fuzz 1400)")
|
|
{
|
|
json _;
|
|
std::vector<uint8_t> const vec = {'"', '\\', '"', 'X', '"', '"'};
|
|
CHECK_THROWS_AS(_ = json::parse(vec), json::parse_error&);
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #600 - how does one convert a map in Json back to std::map?")
|
|
{
|
|
SECTION("example 1")
|
|
{
|
|
// create a map
|
|
const std::map<std::string, int> m1 {{"key", 1}};
|
|
|
|
// create and print a JSON from the map
|
|
json const j = m1;
|
|
|
|
// get the map out of JSON
|
|
std::map<std::string, int> m2 = j;
|
|
|
|
// make sure the roundtrip succeeds
|
|
CHECK(m1 == m2);
|
|
}
|
|
|
|
SECTION("example 2")
|
|
{
|
|
// create a map
|
|
const std::map<std::string, std::string> m1 {{"key", "val"}};
|
|
|
|
// create and print a JSON from the map
|
|
json const j = m1;
|
|
|
|
// get the map out of JSON
|
|
std::map<std::string, std::string> m2 = j;
|
|
|
|
// make sure the roundtrip succeeds
|
|
CHECK(m1 == m2);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #602 - BOM not skipped when using json:parse(iterator)")
|
|
{
|
|
std::string i = "\xef\xbb\xbf{\n \"foo\": true\n}";
|
|
json _;
|
|
CHECK_NOTHROW(_ = json::parse(i.begin(), i.end()));
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #702 - conversion from valarray<double> to json fails to build")
|
|
{
|
|
SECTION("original example")
|
|
{
|
|
std::valarray<double> const v;
|
|
nlohmann::json j;
|
|
j["test"] = v;
|
|
}
|
|
|
|
SECTION("full example")
|
|
{
|
|
std::valarray<double> v = {1.2, 2.3, 3.4, 4.5};
|
|
json j = v;
|
|
std::valarray<double> vj = j;
|
|
|
|
CHECK(j == json(vj));
|
|
CHECK(v.size() == vj.size());
|
|
for (size_t i = 0; i < v.size(); ++i)
|
|
{
|
|
CHECK(v[i] == vj[i]);
|
|
CHECK(v[i] == j[i]);
|
|
}
|
|
|
|
CHECK_THROWS_WITH_AS(json().get<std::valarray<double>>(), "[json.exception.type_error.302] type must be array, but is null", json::type_error&);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #367 - Behavior of operator>> should more closely resemble that of built-in overloads.")
|
|
{
|
|
SECTION("example 1")
|
|
{
|
|
std::istringstream i1_2_3( R"({"first": "one" }{"second": "two"}3)" );
|
|
json j1;
|
|
json j2;
|
|
json j3;
|
|
i1_2_3 >> j1;
|
|
i1_2_3 >> j2;
|
|
i1_2_3 >> j3;
|
|
|
|
auto m1 = j1.get<std::map<std::string, std::string>>();
|
|
auto m2 = j2.get<std::map<std::string, std::string>>();
|
|
const int i3{j3};
|
|
|
|
CHECK( m1 == ( std::map<std::string, std::string> {{ "first", "one" }} ));
|
|
CHECK( m2 == ( std::map<std::string, std::string> {{ "second", "two" }} ));
|
|
CHECK( i3 == 3 );
|
|
}
|
|
}
|
|
|
|
SECTION("issue #714 - throw std::ios_base::failure exception when failbit set to true")
|
|
{
|
|
{
|
|
std::ifstream is;
|
|
is.exceptions(
|
|
is.exceptions()
|
|
| std::ios_base::failbit
|
|
| std::ios_base::badbit
|
|
); // handle different exceptions as 'file not found', 'permission denied'
|
|
|
|
is.open(TEST_DATA_DIRECTORY "/regression/working_file.json");
|
|
json _;
|
|
CHECK_NOTHROW(_ = nlohmann::json::parse(is));
|
|
}
|
|
|
|
{
|
|
std::ifstream is;
|
|
is.exceptions(
|
|
is.exceptions()
|
|
| std::ios_base::failbit
|
|
| std::ios_base::badbit
|
|
); // handle different exceptions as 'file not found', 'permission denied'
|
|
|
|
is.open(TEST_DATA_DIRECTORY "/json_nlohmann_tests/all_unicode.json.cbor",
|
|
std::ios_base::in | std::ios_base::binary);
|
|
json _;
|
|
CHECK_NOTHROW(_ = nlohmann::json::from_cbor(is));
|
|
}
|
|
}
|
|
|
|
SECTION("issue #805 - copy constructor is used with std::initializer_list constructor.")
|
|
{
|
|
nocopy n;
|
|
json j;
|
|
j = {{"nocopy", n}};
|
|
CHECK(j["nocopy"]["val"] == 0);
|
|
}
|
|
|
|
SECTION("issue #838 - incorrect parse error with binary data in keys")
|
|
{
|
|
std::array<uint8_t, 28> key1 = {{ 103, 92, 117, 48, 48, 48, 55, 92, 114, 215, 126, 214, 95, 92, 34, 174, 40, 71, 38, 174, 40, 71, 38, 223, 134, 247, 127, 0 }};
|
|
std::string const key1_str(reinterpret_cast<char*>(key1.data()));
|
|
json const j = key1_str;
|
|
// dump() is nodiscard; the exception is thrown by dump() itself before it would return
|
|
CHECK_THROWS_WITH_AS(utils::ignore_return_value(j.dump()), "[json.exception.type_error.316] invalid UTF-8 byte at index 10: 0x7E", json::type_error&);
|
|
}
|
|
|
|
#if JSON_USE_IMPLICIT_CONVERSIONS
|
|
SECTION("issue #843 - converting to array not working")
|
|
{
|
|
json j;
|
|
std::array<int, 4> ar = {{1, 1, 1, 1}};
|
|
j = ar;
|
|
ar = j;
|
|
}
|
|
#endif
|
|
|
|
SECTION("issue #894 - invalid RFC6902 copy operation succeeds")
|
|
{
|
|
auto model = R"({
|
|
"one": {
|
|
"two": {
|
|
"three": "hello",
|
|
"four": 42
|
|
}
|
|
}
|
|
})"_json;
|
|
|
|
auto p1 = R"([{"op": "move",
|
|
"from": "/one/two/three",
|
|
"path": "/a/b/c"}])"_json;
|
|
#if JSON_DIAGNOSTIC_POSITIONS
|
|
CHECK_THROWS_WITH_AS(model.patch(p1),
|
|
"[json.exception.out_of_range.403] (bytes 0-158) key 'a' not found", json::out_of_range&);
|
|
#else
|
|
CHECK_THROWS_WITH_AS(model.patch(p1),
|
|
"[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&);
|
|
#endif
|
|
|
|
auto p2 = R"([{"op": "copy",
|
|
"from": "/one/two/three",
|
|
"path": "/a/b/c"}])"_json;
|
|
#if JSON_DIAGNOSTIC_POSITIONS
|
|
CHECK_THROWS_WITH_AS(model.patch(p2),
|
|
"[json.exception.out_of_range.403] (bytes 0-158) key 'a' not found", json::out_of_range&);
|
|
#else
|
|
CHECK_THROWS_WITH_AS(model.patch(p2),
|
|
"[json.exception.out_of_range.403] key 'a' not found", json::out_of_range&);
|
|
#endif
|
|
}
|
|
|
|
SECTION("issue #961 - incorrect parsing of indefinite length CBOR strings")
|
|
{
|
|
std::vector<uint8_t> const v_cbor =
|
|
{
|
|
0x7F,
|
|
0x64,
|
|
'a', 'b', 'c', 'd',
|
|
0x63,
|
|
'1', '2', '3',
|
|
0xFF
|
|
};
|
|
const json j = json::from_cbor(v_cbor);
|
|
CHECK(j == "abcd123");
|
|
}
|
|
|
|
SECTION("issue #962 - Timeout (OSS-Fuzz 6034)")
|
|
{
|
|
json _;
|
|
std::vector<uint8_t> v_ubjson = {'[', '$', 'Z', '#', 'L', 0x78, 0x28, 0x00, 0x68, 0x28, 0x69, 0x69, 0x17};
|
|
CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&);
|
|
//CHECK_THROWS_WITH(json::from_ubjson(v_ubjson),
|
|
// "[json.exception.out_of_range.408] excessive array size: 8658170730974374167");
|
|
|
|
v_ubjson[0] = '{';
|
|
CHECK_THROWS_AS(_ = json::from_ubjson(v_ubjson), json::out_of_range&);
|
|
//CHECK_THROWS_WITH(json::from_ubjson(v_ubjson),
|
|
// "[json.exception.out_of_range.408] excessive object size: 8658170730974374167");
|
|
}
|
|
|
|
SECTION("issue #971 - Add a SAX parser - late bug")
|
|
{
|
|
// a JSON text
|
|
const auto* text = R"(
|
|
{
|
|
"Image": {
|
|
"Width": 800,
|
|
"Height": 600,
|
|
"Title": "View from 15th Floor",
|
|
"Thumbnail": {
|
|
"Url": "http://www.example.com/image/481989943",
|
|
"Height": 125,
|
|
"Width": 100
|
|
},
|
|
"Animated" : false,
|
|
"IDs": [116, 943, 234, 38793]
|
|
}
|
|
}
|
|
)";
|
|
|
|
// define parser callback
|
|
json::parser_callback_t const cb = [](int /*depth*/, json::parse_event_t event, json & parsed)
|
|
{
|
|
// skip object elements with key "Thumbnail"
|
|
return !(event == json::parse_event_t::key && parsed == json("Thumbnail"));
|
|
};
|
|
|
|
// parse (with callback) and serialize JSON
|
|
const json j_filtered = json::parse(text, cb);
|
|
|
|
CHECK(j_filtered == R"({"Image":{"Animated":false,"Height":600,"IDs":[116,943,234,38793], "Title":"View from 15th Floor","Width":800}})"_json);
|
|
}
|
|
|
|
SECTION("issue #972 - Segmentation fault on G++ when trying to assign json string literal to custom json type")
|
|
{
|
|
// this assignment used to crash outright
|
|
my_json const foo = R"([1, 2, 3])"_json;
|
|
|
|
// fifo_map is the adapter the docs recommend for keeping object keys
|
|
// in insertion order (see docs/mkdocs/docs/features/object_order.md
|
|
// and docs/mkdocs/docs/features/types/template_parameters.md); check
|
|
// that recommendation actually holds, including through erase() and
|
|
// inserting a new key. The comparator is stateful, so this avoids
|
|
// deep copies of "order" (see #1763, #5649).
|
|
my_json order = my_json::parse(R"({"z":1,"a":2,"m":{"y":1,"b":2}})");
|
|
CHECK(order.dump() == R"({"z":1,"a":2,"m":{"y":1,"b":2}})");
|
|
|
|
order.erase("z");
|
|
CHECK(order.dump() == R"({"a":2,"m":{"y":1,"b":2}})");
|
|
|
|
order["new_key"] = 3;
|
|
CHECK(order.dump() == R"({"a":2,"m":{"y":1,"b":2},"new_key":3})");
|
|
}
|
|
|
|
SECTION("issue #977 - Assigning between different json types")
|
|
{
|
|
foo_json lj = ns::foo{3};
|
|
const ns::foo ff(lj);
|
|
CHECK(lj.is_object());
|
|
CHECK(lj.size() == 1);
|
|
CHECK(lj["x"] == 3);
|
|
CHECK(ff.x == 3);
|
|
nlohmann::json const nj = lj; // This line works as expected
|
|
}
|
|
}
|
|
|
|
#if !defined(JSON_NOEXCEPTION)
|
|
TEST_CASE("regression tests, exceptions dependent")
|
|
{
|
|
SECTION("issue #1340 - eof not set on exhausted input stream")
|
|
{
|
|
std::stringstream s("{}{}");
|
|
json j;
|
|
s >> j;
|
|
s >> j;
|
|
CHECK_THROWS_AS(s >> j, json::parse_error const&);
|
|
CHECK(s.eof());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
// for #1642
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// the code below fails with Clang on Windows, so we need to exclude it there
|
|
#if DOCTEST_CLANG && (defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__))
|
|
#else
|
|
template <typename T> class array {};
|
|
template <typename T> class object {};
|
|
template <typename T> class string {};
|
|
template <typename T> class number_integer {};
|
|
template <typename T> class number_unsigned {};
|
|
template <typename T> class number_float {};
|
|
#endif
|