diff --git a/src/json.hpp b/src/json.hpp index a9da694c..af8e490d 100644 --- a/src/json.hpp +++ b/src/json.hpp @@ -587,6 +587,95 @@ class basic_json alloc.construct(m_value.array, count, other); } + // construct a JSON container given an iterator range + template ::value or + std::is_same::value + , int>::type + = 0> + inline basic_json(T first, T last) + { + // make sure iterator fits the current value + if (first.m_object != last.m_object or + first.m_object->m_type != last.m_object->m_type) + { + throw std::runtime_error("iterators are not compatible"); + } + + m_type = first.m_object->m_type; + + switch (m_type) + { + case value_t::number_integer: + case value_t::number_float: + case value_t::boolean: + case value_t::string: + { + if (first.m_it.generic_iterator != 0 or last.m_it.generic_iterator != 1) + { + throw std::out_of_range("iterators out of range"); + } + break; + } + + default: + { + break; + } + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + AllocatorType alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, *first.m_object->m_value.string); + break; + } + + case value_t::object: + { + AllocatorType alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object, first.m_it.object_iterator, last.m_it.object_iterator); + break; + } + + case value_t::array: + { + AllocatorType alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + default: + { + throw std::runtime_error("cannot use construct with iterators from " + first.m_object->type_name()); + } + } + } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c index 58d80d9d..2ad9924c 100644 --- a/src/json.hpp.re2c +++ b/src/json.hpp.re2c @@ -587,6 +587,97 @@ class basic_json alloc.construct(m_value.array, count, other); } + /// construct a JSON container given an iterator range + template ::value or + std::is_same::value + , int>::type + = 0> + inline basic_json(T first, T last) + { + // make sure iterator fits the current value + if (first.m_object != last.m_object or + first.m_object->m_type != last.m_object->m_type) + { + throw std::runtime_error("iterators are not compatible"); + } + + // set the type + m_type = first.m_object->m_type; + + // check if iterator range is complete for non-compound values + switch (m_type) + { + case value_t::number_integer: + case value_t::number_float: + case value_t::boolean: + case value_t::string: + { + if (first.m_it.generic_iterator != 0 or last.m_it.generic_iterator != 1) + { + throw std::out_of_range("iterators out of range"); + } + break; + } + + default: + { + break; + } + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + AllocatorType alloc; + m_value.string = alloc.allocate(1); + alloc.construct(m_value.string, *first.m_object->m_value.string); + break; + } + + case value_t::object: + { + AllocatorType alloc; + m_value.object = alloc.allocate(1); + alloc.construct(m_value.object, first.m_it.object_iterator, last.m_it.object_iterator); + break; + } + + case value_t::array: + { + AllocatorType alloc; + m_value.array = alloc.allocate(1); + alloc.construct(m_value.array, first.m_it.array_iterator, last.m_it.array_iterator); + break; + } + + default: + { + throw std::runtime_error("cannot use construct with iterators from " + first.m_object->type_name()); + } + } + } + /////////////////////////////////////// // other constructors and destructor // /////////////////////////////////////// diff --git a/test/unit.cpp b/test/unit.cpp index e251f776..fe6a31b0 100644 --- a/test/unit.cpp +++ b/test/unit.cpp @@ -913,6 +913,257 @@ TEST_CASE("constructors") CHECK(x == v); } } + + SECTION("create a JSON container from an iterator range") + { + SECTION("object") + { + SECTION("json(begin(), end())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json j_new(jobject.begin(), jobject.end()); + CHECK(j_new == jobject); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json j_new(jobject.cbegin(), jobject.cend()); + CHECK(j_new == jobject); + } + } + + SECTION("json(begin(), begin())") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json j_new(jobject.begin(), jobject.begin()); + CHECK(j_new == json::object()); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}}; + json j_new(jobject.cbegin(), jobject.cbegin()); + CHECK(j_new == json::object()); + } + } + + SECTION("construct from subrange") + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json j_new(jobject.find("b"), jobject.find("e")); + CHECK(j_new == json({{"b", 1}, {"c", 17}, {"d", false}})); + } + + SECTION("incompatible iterators") + { + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::runtime_error); + CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::runtime_error); + } + { + json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}}; + json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17}}; + CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::runtime_error); + CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::runtime_error); + } + } + } + + SECTION("array") + { + SECTION("json(begin(), end())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.end()); + CHECK(j_new == jarray); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cend()); + CHECK(j_new == jarray); + } + } + + SECTION("json(begin(), begin())") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin(), jarray.begin()); + CHECK(j_new == json::array()); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin(), jarray.cbegin()); + CHECK(j_new == json::array()); + } + } + + SECTION("construct from subrange") + { + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.begin() + 1, jarray.begin() + 3); + CHECK(j_new == json({2, 3})); + } + { + json jarray = {1, 2, 3, 4, 5}; + json j_new(jarray.cbegin() + 1, jarray.cbegin() + 3); + CHECK(j_new == json({2, 3})); + } + } + + SECTION("incompatible iterators") + { + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::runtime_error); + CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::runtime_error); + } + { + json jarray = {1, 2, 3, 4}; + json jarray2 = {2, 3, 4, 5}; + CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::runtime_error); + CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::runtime_error); + } + } + } + + SECTION("other values") + { + SECTION("construct with two valid iterators") + { + SECTION("null") + { + { + json j; + CHECK_THROWS_AS(json(j.begin(), j.end()), std::runtime_error); + } + { + json j; + CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::runtime_error); + } + } + + SECTION("string") + { + { + json j = "foo"; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = "bar"; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = true; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 17; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + json j_new(j.begin(), j.end()); + CHECK(j == j_new); + } + { + json j = 23.42; + json j_new(j.cbegin(), j.cend()); + CHECK(j == j_new); + } + } + } + + SECTION("construct with two invalid iterators") + { + SECTION("string") + { + { + json j = "foo"; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + } + { + json j = "bar"; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + } + } + + SECTION("number (boolean)") + { + { + json j = false; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + } + { + json j = true; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + } + } + + SECTION("number (integer)") + { + { + json j = 17; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + } + { + json j = 17; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + } + } + + SECTION("number (floating point)") + { + { + json j = 23.42; + CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range); + CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range); + } + { + json j = 23.42; + CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range); + CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range); + } + } + } + } + } } TEST_CASE("other constructors and destructor") @@ -2807,7 +3058,7 @@ TEST_CASE("element access") } } - SECTION("different arrays") + SECTION("different objects") { { json jobject = {{"a", "a"}, {"b", 1}, {"c", 17}, {"d", false}, {"e", true}};