// __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ (supporting code) // | | |__ | | | | | | version 3.11.2 // |_____|_____|_____|_|___| https://github.com/nlohmann/json // // Copyright (c) 2013-2022 Niels Lohmann . // SPDX-FileCopyrightText: 2013-2022 Niels Lohmann // SPDX-License-Identifier: MIT #include "doctest_compatibility.h" #include using nlohmann::json; TEST_CASE("modifiers") { SECTION("clear()") { SECTION("boolean") { json j = true; json k = j; j.clear(); CHECK(j == json(json::value_t::boolean)); CHECK(j == json(k.type())); } SECTION("string") { json j = "hello world"; json k = j; j.clear(); CHECK(j == json(json::value_t::string)); CHECK(j == json(k.type())); } SECTION("array") { SECTION("empty array") { json j = json::array(); json k = j; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::array)); CHECK(j == json(k.type())); } SECTION("filled array") { json j = {1, 2, 3}; json k = j; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::array)); CHECK(j == json(k.type())); } } SECTION("object") { SECTION("empty object") { json j = json::object(); json k = j; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::object)); CHECK(j == json(k.type())); } SECTION("filled object") { json j = {{"one", 1}, {"two", 2}, {"three", 3}}; json k = j; j.clear(); CHECK(j.empty()); CHECK(j == json(json::value_t::object)); CHECK(j == json(k.type())); } } SECTION("binary") { SECTION("empty binary") { json j = json::binary({}); json k = j; j.clear(); CHECK(!j.empty()); CHECK(j == json(json::value_t::binary)); CHECK(j == json(k.type())); } SECTION("filled binary") { json j = json::binary({1, 2, 3, 4, 5}); json k = j; j.clear(); CHECK(!j.empty()); CHECK(j == json(json::value_t::binary)); CHECK(j == json(k.type())); } } SECTION("number (integer)") { json j = 23; json k = j; j.clear(); CHECK(j == json(json::value_t::number_integer)); CHECK(j == json(k.type())); } SECTION("number (unsigned)") { json j = 23u; json k = j; j.clear(); CHECK(j == json(json::value_t::number_integer)); CHECK(j == json(k.type())); } SECTION("number (float)") { json j = 23.42; json k = j; j.clear(); CHECK(j == json(json::value_t::number_float)); CHECK(j == json(k.type())); } SECTION("null") { json j = nullptr; json k = j; j.clear(); CHECK(j == json(json::value_t::null)); CHECK(j == json(k.type())); } } SECTION("push_back()") { SECTION("to array") { SECTION("json&&") { SECTION("null") { json j; j.push_back(1); j.push_back(2); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2})); } SECTION("array") { json j = {1, 2, 3}; j.push_back("Hello"); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; CHECK_THROWS_WITH_AS(j.push_back("Hello"), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } SECTION("const json&") { SECTION("null") { json j; json k(1); j.push_back(k); j.push_back(k); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 1})); } SECTION("array") { json j = {1, 2, 3}; json k("Hello"); j.push_back(k); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_WITH_AS(j.push_back(k), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } } SECTION("to object") { SECTION("null") { json j; j.push_back(json::object_t::value_type({"one", 1})); j.push_back(json::object_t::value_type({"two", 2})); CHECK(j.type() == json::value_t::object); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("object") { json j(json::value_t::object); j.push_back(json::object_t::value_type({"one", 1})); j.push_back(json::object_t::value_type({"two", 2})); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_WITH_AS(j.push_back(json::object_t::value_type({"one", 1})), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } SECTION("with initializer_list") { SECTION("null") { json j; j.push_back({"foo", "bar"}); CHECK(j == json::array({{"foo", "bar"}})); json k; k.push_back({1, 2, 3}); CHECK(k == json::array({{1, 2, 3}})); } SECTION("array") { json j = {1, 2, 3}; j.push_back({"foo", "bar"}); CHECK(j == json({1, 2, 3, {"foo", "bar"}})); json k = {1, 2, 3}; k.push_back({1, 2, 3}); CHECK(k == json({1, 2, 3, {1, 2, 3}})); } SECTION("object") { json j = {{"key1", 1}}; j.push_back({"key2", "bar"}); CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); // invalid values (no string/val pair) CHECK_THROWS_WITH_AS(j.push_back({1}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); CHECK_THROWS_WITH_AS(j.push_back({1, 2}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); CHECK_THROWS_WITH_AS(j.push_back({1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); } } } SECTION("emplace_back()") { SECTION("to array") { SECTION("null") { json j; auto& x1 = j.emplace_back(1); CHECK(x1 == 1); auto& x2 = j.emplace_back(2); CHECK(x2 == 2); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2})); } SECTION("array") { json j = {1, 2, 3}; auto& x = j.emplace_back("Hello"); CHECK(x == "Hello"); CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("multiple values") { json j; auto& x = j.emplace_back(3, "foo"); CHECK(x == json({"foo", "foo", "foo"})); CHECK(j.type() == json::value_t::array); CHECK(j == json({{"foo", "foo", "foo"}})); } } SECTION("other type") { json j = 1; CHECK_THROWS_WITH_AS(j.emplace_back("Hello"), "[json.exception.type_error.311] cannot use emplace_back() with number", json::type_error&); } } SECTION("emplace()") { SECTION("to object") { SECTION("null") { // start with a null value json j; // add a new key auto res1 = j.emplace("foo", "bar"); CHECK(res1.second == true); CHECK(*res1.first == "bar"); // the null value is changed to an object CHECK(j.type() == json::value_t::object); // add a new key auto res2 = j.emplace("baz", "bam"); CHECK(res2.second == true); CHECK(*res2.first == "bam"); // we try to insert at given key - no change auto res3 = j.emplace("baz", "bad"); CHECK(res3.second == false); CHECK(*res3.first == "bam"); // the final object CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}})); } SECTION("object") { // start with an object json j = {{"foo", "bar"}}; // add a new key auto res1 = j.emplace("baz", "bam"); CHECK(res1.second == true); CHECK(*res1.first == "bam"); // add an existing key auto res2 = j.emplace("foo", "bad"); CHECK(res2.second == false); CHECK(*res2.first == "bar"); // check final object CHECK(j == json({{"baz", "bam"}, {"foo", "bar"}})); } } SECTION("other type") { json j = 1; CHECK_THROWS_WITH_AS(j.emplace("foo", "bar"), "[json.exception.type_error.311] cannot use emplace() with number", json::type_error&); } } SECTION("operator+=") { SECTION("to array") { SECTION("json&&") { SECTION("null") { json j; j += 1; j += 2; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2})); } SECTION("array") { json j = {1, 2, 3}; j += "Hello"; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; CHECK_THROWS_WITH_AS(j += "Hello", "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } SECTION("const json&") { SECTION("null") { json j; json k(1); j += k; j += k; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 1})); } SECTION("array") { json j = {1, 2, 3}; json k("Hello"); j += k; CHECK(j.type() == json::value_t::array); CHECK(j == json({1, 2, 3, "Hello"})); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_WITH_AS(j += k, "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } } SECTION("to object") { SECTION("null") { json j; j += json::object_t::value_type({"one", 1}); j += json::object_t::value_type({"two", 2}); CHECK(j.type() == json::value_t::object); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("object") { json j(json::value_t::object); j += json::object_t::value_type({"one", 1}); j += json::object_t::value_type({"two", 2}); CHECK(j.size() == 2); CHECK(j["one"] == json(1)); CHECK(j["two"] == json(2)); } SECTION("other type") { json j = 1; json k("Hello"); CHECK_THROWS_WITH_AS(j += json::object_t::value_type({"one", 1}), "[json.exception.type_error.308] cannot use push_back() with number", json::type_error&); } } SECTION("with initializer_list") { SECTION("null") { json j; j += {"foo", "bar"}; CHECK(j == json::array({{"foo", "bar"}})); json k; k += {1, 2, 3}; CHECK(k == json::array({{1, 2, 3}})); } SECTION("array") { json j = {1, 2, 3}; j += {"foo", "bar"}; CHECK(j == json({1, 2, 3, {"foo", "bar"}})); json k = {1, 2, 3}; k += {1, 2, 3}; CHECK(k == json({1, 2, 3, {1, 2, 3}})); } SECTION("object") { json j = {{"key1", 1}}; j += {"key2", "bar"}; CHECK(j == json({{"key1", 1}, {"key2", "bar"}})); json k = {{"key1", 1}}; CHECK_THROWS_WITH_AS((k += {1, 2, 3, 4}), "[json.exception.type_error.308] cannot use push_back() with object", json::type_error&); } } } SECTION("insert()") { json j_array = {1, 2, 3, 4}; json j_value = 5; SECTION("value at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), j_value); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((j_array.end() - it) == 1); CHECK(j_array == json({1, 2, 3, 4, 5})); } } SECTION("rvalue at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), 5); CHECK(j_array.size() == 5); CHECK(*it == j_value); CHECK((j_array.end() - it) == 1); CHECK(j_array == json({1, 2, 3, 4, 5})); } } SECTION("copies at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK(j_array.begin() == it); CHECK(j_array == json({5, 5, 5, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 5, 5, 5, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), 3, 5); CHECK(j_array.size() == 7); CHECK(*it == j_value); CHECK((j_array.end() - it) == 3); CHECK(j_array == json({1, 2, 3, 4, 5, 5, 5})); } SECTION("insert nothing (count = 0)") { auto it = j_array.insert(j_array.end(), 0, 5); CHECK(j_array.size() == 4); // the returned iterator points to the first inserted element; // there were 4 elements, so it should point to the 5th CHECK(it == j_array.begin() + 4); CHECK(j_array == json({1, 2, 3, 4})); } } SECTION("range for array") { json j_other_array = {"first", "second"}; SECTION("proper usage") { auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.end()); CHECK(j_array.size() == 6); CHECK(*it == *j_other_array.begin()); CHECK((j_array.end() - it) == 2); CHECK(j_array == json({1, 2, 3, 4, "first", "second"})); } SECTION("empty range") { auto it = j_array.insert(j_array.end(), j_other_array.begin(), j_other_array.begin()); CHECK(j_array.size() == 4); CHECK(it == j_array.end()); CHECK(j_array == json({1, 2, 3, 4})); } SECTION("invalid iterators") { json j_other_array2 = {"first", "second"}; CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.211] passed iterators may not belong to container", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&); } } SECTION("range for object") { json j_object1 = {{"one", "eins"}, {"two", "zwei"}}; json j_object2 = {{"eleven", "elf"}, {"seventeen", "siebzehn"}}; SECTION("proper usage") { j_object1.insert(j_object2.begin(), j_object2.end()); CHECK(j_object1.size() == 4); } SECTION("empty range") { j_object1.insert(j_object2.begin(), j_object2.begin()); CHECK(j_object1.size() == 2); } SECTION("invalid iterators") { json j_other_array2 = {"first", "second"}; CHECK_THROWS_WITH_AS(j_array.insert(j_object2.begin(), j_object2.end()), "[json.exception.type_error.309] cannot use insert() with array", json::type_error&); CHECK_THROWS_WITH_AS(j_object1.insert(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_object1.insert(j_array.begin(), j_array.end()), "[json.exception.invalid_iterator.202] iterators first and last must point to objects", json::invalid_iterator&); } } SECTION("initializer list at position") { SECTION("insert before begin()") { auto it = j_array.insert(j_array.begin(), {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK(j_array.begin() == it); CHECK(j_array == json({7, 8, 9, 1, 2, 3, 4})); } SECTION("insert in the middle") { auto it = j_array.insert(j_array.begin() + 2, {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK((it - j_array.begin()) == 2); CHECK(j_array == json({1, 2, 7, 8, 9, 3, 4})); } SECTION("insert before end()") { auto it = j_array.insert(j_array.end(), {7, 8, 9}); CHECK(j_array.size() == 7); CHECK(*it == json(7)); CHECK((j_array.end() - it) == 3); CHECK(j_array == json({1, 2, 3, 4, 7, 8, 9})); } } SECTION("invalid iterator") { // pass iterator to a different array json j_another_array = {1, 2}; json j_yet_another_array = {"first", "second"}; CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_value), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), 10, 11), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), "[json.exception.invalid_iterator.202] iterator does not fit current value", json::invalid_iterator&); } SECTION("non-array type") { // call insert on a non-array type json j_nonarray = 3; json j_yet_another_array = {"first", "second"}; CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_value), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(), j_yet_another_array.end()), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); CHECK_THROWS_WITH_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), "[json.exception.type_error.309] cannot use insert() with number", json::type_error&); } } SECTION("update()") { SECTION("non-recursive (default)") { json j_object1 = {{"one", "eins"}, {"two", "zwei"}}; json j_object2 = {{"three", "drei"}, {"two", "zwo"}}; json j_array = {1, 2, 3, 4}; SECTION("const reference") { SECTION("proper usage") { j_object1.update(j_object2); CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}})); json j_null; j_null.update(j_object2); CHECK(j_null == j_object2); } SECTION("wrong types") { CHECK_THROWS_WITH_AS(j_array.update(j_object1), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); CHECK_THROWS_WITH_AS(j_object1.update(j_array), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); } } SECTION("iterator range") { SECTION("proper usage") { j_object1.update(j_object2.begin(), j_object2.end()); CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwo"}, {"three", "drei"}})); json j_null; j_null.update(j_object2.begin(), j_object2.end()); CHECK(j_null == j_object2); } SECTION("empty range") { j_object1.update(j_object2.begin(), j_object2.begin()); CHECK(j_object1 == json({{"one", "eins"}, {"two", "zwei"}})); } SECTION("invalid iterators") { json j_other_array2 = {"first", "second"}; CHECK_THROWS_WITH_AS(j_array.update(j_object2.begin(), j_object2.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); CHECK_THROWS_WITH_AS(j_object1.update(j_object1.begin(), j_object2.end()), "[json.exception.invalid_iterator.210] iterators do not fit", json::invalid_iterator&); CHECK_THROWS_WITH_AS(j_object1.update(j_array.begin(), j_array.end()), "[json.exception.type_error.312] cannot use update() with array", json::type_error&); } } } SECTION("recursive") { SECTION("const reference") { SECTION("extend object") { json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}}; json j2 = {{"string", "t"}, {"numbers", {{"two", 2}}}}; j1.update(j2, true); CHECK(j1 == json({{"string", "t"}, {"numbers", {{"one", 1}, {"two", 2}}}})); } SECTION("replace object") { json j1 = {{"string", "s"}, {"numbers", {{"one", 1}}}}; json j2 = {{"string", "t"}, {"numbers", 1}}; j1.update(j2, true); CHECK(j1 == json({{"string", "t"}, {"numbers", 1}})); } } } } SECTION("swap()") { SECTION("json") { SECTION("member swap") { json j("hello world"); json k(42.23); j.swap(k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); } SECTION("nonmember swap") { json j("hello world"); json k(42.23); using std::swap; swap(j, k); CHECK(j == json(42.23)); CHECK(k == json("hello world")); } } SECTION("array_t") { SECTION("array_t type") { json j = {1, 2, 3, 4}; json::array_t a = {"foo", "bar", "baz"}; j.swap(a); CHECK(j == json({"foo", "bar", "baz"})); j.swap(a); CHECK(j == json({1, 2, 3, 4})); } SECTION("non-array_t type") { json j = 17; json::array_t a = {"foo", "bar", "baz"}; CHECK_THROWS_WITH_AS(j.swap(a), "[json.exception.type_error.310] cannot use swap(array_t&) with number", json::type_error&); } } SECTION("object_t") { SECTION("object_t type") { json j = {{"one", 1}, {"two", 2}}; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; j.swap(o); CHECK(j == json({{"cow", "Kuh"}, {"chicken", "Huhn"}})); j.swap(o); CHECK(j == json({{"one", 1}, {"two", 2}})); } SECTION("non-object_t type") { json j = 17; json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}}; CHECK_THROWS_WITH_AS(j.swap(o), "[json.exception.type_error.310] cannot use swap(object_t&) with number", json::type_error&); } } SECTION("string_t") { SECTION("string_t type") { json j = "Hello world"; json::string_t s = "Hallo Welt"; j.swap(s); CHECK(j == json("Hallo Welt")); j.swap(s); CHECK(j == json("Hello world")); } SECTION("non-string_t type") { json j = 17; json::string_t s = "Hallo Welt"; CHECK_THROWS_WITH_AS(j.swap(s), "[json.exception.type_error.310] cannot use swap(string_t&) with number", json::type_error&); } } SECTION("binary_t") { SECTION("binary_t type") { json j = json::binary({1, 2, 3, 4}); json::binary_t s = {{5, 6, 7, 8}}; j.swap(s); CHECK(j == json::binary({5, 6, 7, 8})); j.swap(s); CHECK(j == json::binary({1, 2, 3, 4})); } SECTION("binary_t::container_type type") { json j = json::binary({1, 2, 3, 4}); std::vector s = {{5, 6, 7, 8}}; j.swap(s); CHECK(j == json::binary({5, 6, 7, 8})); j.swap(s); CHECK(j == json::binary({1, 2, 3, 4})); } SECTION("non-binary_t type") { json j = 17; json::binary_t s1 = {{1, 2, 3, 4}}; std::vector s2 = {{5, 6, 7, 8}}; CHECK_THROWS_WITH_AS(j.swap(s1), "[json.exception.type_error.310] cannot use swap(binary_t&) with number", json::type_error); CHECK_THROWS_WITH_AS(j.swap(s2), "[json.exception.type_error.310] cannot use swap(binary_t::container_type&) with number", json::type_error); } } } }