mirror of
https://github.com/nlohmann/json.git
synced 2026-10-10 16:37:14 +00:00
Merge branch 'json-view/21-images' into json-view/22-view-dump-fast
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
25 files changed
+378
-285
No files matched your search
@@ -577,7 +577,7 @@ std::string exception_of(F f)
|
||||
|
||||
// compares a view with the ordered_json value materialize() gives for it:
|
||||
// types, sizes, elements and members (by index, key, and iteration), in
|
||||
// document order; duplicate keys are found as their last occurrence
|
||||
// document order; duplicate keys are found as their first occurrence
|
||||
void check_access(const ordered_json_view& v, const ordered_json& j)
|
||||
{
|
||||
REQUIRE(v.type() == j.type());
|
||||
@@ -618,23 +618,13 @@ void check_access(const ordered_json_view& v, const ordered_json& j)
|
||||
const std::string key(it.key().data(), it.key().size());
|
||||
CHECK(v.contains(key));
|
||||
CHECK(v.count(key) == 1);
|
||||
if (std::find(keys.begin(), keys.end(), key) == keys.end())
|
||||
{
|
||||
keys.push_back(key);
|
||||
}
|
||||
// lookups find the last member with the key, which is this one if
|
||||
// there is no later one
|
||||
auto next = it;
|
||||
++next;
|
||||
bool is_last = true;
|
||||
for (; next != v.end(); ++next)
|
||||
{
|
||||
is_last = is_last && next.key() != it.key();
|
||||
}
|
||||
if (!is_last)
|
||||
// lookups find the first member with the key, which is this one if
|
||||
// there is no earlier one
|
||||
if (std::find(keys.begin(), keys.end(), key) != keys.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
keys.push_back(key);
|
||||
CHECK(v.find(key) == it);
|
||||
CHECK(v[key].materialize() == it->materialize());
|
||||
CHECK(v.at(key).materialize() == it.value().materialize());
|
||||
@@ -725,20 +715,28 @@ TEST_CASE("json_view element access and iteration")
|
||||
#endif
|
||||
}
|
||||
|
||||
SECTION("duplicate keys: lookups find the last member, iteration all")
|
||||
SECTION("duplicate keys: lookups find the first member, iteration all")
|
||||
{
|
||||
const json_document d = json_document::parse(R"({"a":1,"b":2,"a":3})");
|
||||
const json_view v = d.root();
|
||||
CHECK(v.size() == 3);
|
||||
CHECK(v["a"].materialize() == 3);
|
||||
CHECK(v.at("a").materialize() == 3);
|
||||
CHECK(v.find("a") == std::next(v.begin(), 2));
|
||||
CHECK(v.find("a").value().materialize() == 3);
|
||||
CHECK(v["a"].materialize() == 1);
|
||||
CHECK(v.at("a").materialize() == 1);
|
||||
CHECK(v.find("a") == v.begin());
|
||||
CHECK(v.find("a").value().materialize() == 1);
|
||||
CHECK(v.find("b") == std::next(v.begin()));
|
||||
CHECK(v.count("a") == 1);
|
||||
CHECK(v.contains("a"));
|
||||
CHECK(v.value("a", 0) == 3);
|
||||
CHECK(v["a"].materialize() == v.materialize()["a"]); // as materialize()
|
||||
CHECK(v.value("a", 0) == 1);
|
||||
CHECK(v.materialize()["a"] == 3); // materialize() keeps the last value, unlike the lookups
|
||||
// JSON pointers resolve to the first member at every level
|
||||
const json_document dp = json_document::parse(R"({"a":{"b":1},"a":{"b":2,"b":3}})");
|
||||
CHECK(dp.root()[json::json_pointer("/a/b")].materialize() == 1);
|
||||
CHECK(dp.root().at(json::json_pointer("/a/b")).materialize() == 1);
|
||||
CHECK(dp.root().value(json::json_pointer("/a/b"), 0) == 1);
|
||||
CHECK(dp.root().materialize()[json::json_pointer("/a/b")] == 3);
|
||||
// get<map> keeps the last value, as materialize()
|
||||
CHECK((v.get<std::map<std::string, int>>() == std::map<std::string, int> {{"a", 3}, {"b", 2}}));
|
||||
// keys of every length class (the 16-byte short compare and memcmp)
|
||||
for (const std::size_t n :
|
||||
{
|
||||
@@ -748,10 +746,10 @@ TEST_CASE("json_view element access and iteration")
|
||||
const std::string key(n, 'k');
|
||||
const json_document dk = json_document::parse("{\"" + key + "\":1,\"" + key + "x\":2,\"" + key + "\":3,\"" + key + "\":4}");
|
||||
CAPTURE(n)
|
||||
CHECK(dk.root()[key].materialize() == 4);
|
||||
CHECK(dk.root().at(key).materialize() == 4);
|
||||
CHECK(dk.root().find(key) == std::next(dk.root().begin(), 3));
|
||||
CHECK(dk.root().value(key, 0) == 4);
|
||||
CHECK(dk.root()[key].materialize() == 1);
|
||||
CHECK(dk.root().at(key).materialize() == 1);
|
||||
CHECK(dk.root().find(key) == dk.root().begin());
|
||||
CHECK(dk.root().value(key, 0) == 1);
|
||||
CHECK(dk.root()[key + "x"].materialize() == 2);
|
||||
}
|
||||
std::string order;
|
||||
@@ -1515,6 +1513,8 @@ TEST_CASE("json_view dump")
|
||||
const json_document d = json_document::parse(R"({"b": 1, "a": 2, "b": 3})");
|
||||
CHECK(d.root().dump() == R"({"b":1,"a":2,"b":3})");
|
||||
CHECK(d.root().dump(1) == "{\n \"b\": 1,\n \"a\": 2,\n \"b\": 3\n}");
|
||||
// dump() writes all members, though the lookup finds the first
|
||||
CHECK(d.root()["b"].dump() == "1");
|
||||
}
|
||||
|
||||
SECTION("deep nesting")
|
||||
@@ -1637,6 +1637,9 @@ TEST_CASE("json_view comparison")
|
||||
const ordered_json_document dup = ordered_json_document::parse(R"({"a": 1, "b": 2, "a": 3})");
|
||||
const ordered_json_document last = ordered_json_document::parse(R"({"a": 3, "b": 2})");
|
||||
CHECK(dup.root() == last.root());
|
||||
// (== compares as parse() resolves duplicates, a lookup finds the first)
|
||||
CHECK(dup.root()["a"].materialize() == 1);
|
||||
CHECK(dup.root()["a"] != last.root()["a"]);
|
||||
const ordered_json_document ab = ordered_json_document::parse(R"({"a": 1, "b": 2})");
|
||||
const ordered_json_document ba = ordered_json_document::parse(R"({"b": 2, "a": 1})");
|
||||
CHECK(ab.root() != ba.root());
|
||||
@@ -1684,31 +1687,26 @@ TEST_CASE("json_view large objects")
|
||||
for (std::size_t i = 0; i < members; ++i)
|
||||
{
|
||||
const std::string key = std::string(i % 23, 'k') + std::to_string(i) + (i % 7 == 0 ? "\n" : "");
|
||||
CHECK(v[key].get<std::size_t>() == i);
|
||||
CHECK(v.contains(key));
|
||||
CHECK(v.find(key).key() == key);
|
||||
CHECK(!v.contains(key + "x"));
|
||||
if (key == "k1")
|
||||
{
|
||||
continue; // repeated below: the last member wins
|
||||
}
|
||||
CHECK(v[key].get<std::size_t>() == i);
|
||||
CHECK(v.at(key).get<std::size_t>() == i);
|
||||
CHECK(!v.contains(key + "x"));
|
||||
}
|
||||
CHECK(v[""].get_string() == "empty key");
|
||||
CHECK(v["k1"].get_string() == "a duplicate of an earlier key"); // the last of duplicate keys, as for small objects
|
||||
CHECK(v.at("k1").get_string() == "a duplicate of an earlier key");
|
||||
CHECK(v["k1"].get<int>() == 1); // the first of duplicate keys, as for small objects
|
||||
CHECK(!v.contains("missing"));
|
||||
CHECK_THROWS_WITH_AS(v.at("missing"), "[json.exception.out_of_range.403] key 'missing' not found", json::out_of_range&);
|
||||
CHECK(v == j);
|
||||
CHECK(v.materialize() == j);
|
||||
}
|
||||
|
||||
SECTION("duplicate keys: the last member wins, with and without a table")
|
||||
SECTION("duplicate keys: the first member wins, with and without a table")
|
||||
{
|
||||
// an object of `total` members: the keys "k0".."k<n-1>" in order, then
|
||||
// three keys repeated twice more (one copy in the middle, one at the
|
||||
// end), and two keys repeated once; the value of a member is its
|
||||
// position, so that the last member of a key can be told apart
|
||||
// position, so that the first member of a key can be told apart
|
||||
struct member
|
||||
{
|
||||
std::string key;
|
||||
@@ -1756,20 +1754,23 @@ TEST_CASE("json_view large objects")
|
||||
REQUIRE(members.size() >= total);
|
||||
REQUIRE(members.size() >= 100);
|
||||
std::string text = "{";
|
||||
std::map<std::string, std::size_t> first;
|
||||
std::map<std::string, std::size_t> last;
|
||||
for (const member& m : members)
|
||||
{
|
||||
text += (text.size() > 1 ? ",\"" : "\"") + m.key + "\":" + std::to_string(m.position);
|
||||
first.insert({m.key, m.position});
|
||||
last[m.key] = m.position;
|
||||
}
|
||||
text += '}';
|
||||
REQUIRE(last.size() < members.size());
|
||||
REQUIRE(first.size() < members.size());
|
||||
|
||||
const json_document d = json_document::parse(text);
|
||||
const json_view v = d.root();
|
||||
const json j = json::parse(text);
|
||||
CHECK(v.size() == members.size()); // every occurrence is visited
|
||||
for (const auto& entry : last)
|
||||
const json m = v.materialize();
|
||||
for (const auto& entry : first)
|
||||
{
|
||||
CAPTURE(entry.first)
|
||||
const std::size_t expected = entry.second;
|
||||
@@ -1784,7 +1785,9 @@ TEST_CASE("json_view large objects")
|
||||
CHECK(v.at(json::json_pointer(pointer)).get<std::size_t>() == expected);
|
||||
CHECK(v.value(json::json_pointer(pointer), std::size_t{0}) == expected);
|
||||
CHECK(v.contains(json::json_pointer(pointer)));
|
||||
CHECK(j[entry.first].get<std::size_t>() == expected); // as materialize() and parse() keep it
|
||||
// materialize() and parse() keep the last value instead
|
||||
CHECK(j[entry.first].get<std::size_t>() == last.at(entry.first));
|
||||
CHECK(m[entry.first].get<std::size_t>() == last.at(entry.first));
|
||||
}
|
||||
CHECK(!v.contains("k"));
|
||||
CHECK(v["missing"].is_discarded());
|
||||
|
||||
@@ -28,6 +28,7 @@ using ptr_t = ordered_json::json_pointer;
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -181,8 +182,10 @@ void compare(const ordered_json_editable_view& v, const ordered_json& j)
|
||||
}
|
||||
}
|
||||
|
||||
// the text of j, in which some objects repeat a key of theirs: before their
|
||||
// members (the real member is then the last), or after (the repeat is)
|
||||
// the text of j, in which some objects repeat a key of theirs after their
|
||||
// members: lookups of the edited document find the first member of a key, so
|
||||
// j (the first members) is what they must agree with, while parse() would
|
||||
// keep the last value
|
||||
std::string text_with_duplicates(const ordered_json& j)
|
||||
{
|
||||
if (j.is_object())
|
||||
@@ -197,10 +200,6 @@ std::string text_with_duplicates(const ordered_json& j)
|
||||
return ordered_json(keys[static_cast<std::size_t>(r(static_cast<int>(keys.size())))]).dump() + ":" + random_value(2).dump();
|
||||
};
|
||||
std::string text = "{";
|
||||
if (!keys.empty() && r(4) == 0)
|
||||
{
|
||||
text += repeated() + ",";
|
||||
}
|
||||
bool first = true;
|
||||
for (const auto& kv : j.items())
|
||||
{
|
||||
@@ -225,10 +224,58 @@ std::string text_with_duplicates(const ordered_json& j)
|
||||
return j.dump();
|
||||
}
|
||||
|
||||
// every lookup of the edited view finds what j holds, although the view may
|
||||
// have several members for a key (j has the last value, at the position of the
|
||||
// first member: what parse() and materialize() make of it)
|
||||
void check_lookups(const ordered_json_editable_view& v, const ordered_json& j)
|
||||
// every lookup of the edited view finds the first member of its key, which
|
||||
// the iteration of the view shows
|
||||
void check_lookups(const ordered_json_editable_view& v)
|
||||
{
|
||||
if (v.is_object())
|
||||
{
|
||||
std::set<std::string> seen;
|
||||
for (const auto item : v.items())
|
||||
{
|
||||
const std::string key(item.key().data(), item.key().size());
|
||||
if (!seen.insert(key).second)
|
||||
{
|
||||
continue; // (not the first member of the key)
|
||||
}
|
||||
const ptr_t ptr = ptr_t() / key;
|
||||
CAPTURE(key)
|
||||
const ordered_json expected = item.value().materialize();
|
||||
const ordered_json_editable_view m = v[key];
|
||||
REQUIRE(!m.is_discarded());
|
||||
CHECK(m.materialize() == expected);
|
||||
CHECK(v.at(key).materialize() == expected);
|
||||
CHECK(v[ptr].materialize() == expected);
|
||||
CHECK(v.at(ptr).materialize() == expected);
|
||||
CHECK(v.contains(key));
|
||||
CHECK(v.contains(ptr));
|
||||
CHECK(v.count(key) == 1);
|
||||
const auto it = v.find(key);
|
||||
REQUIRE(it != v.end());
|
||||
CHECK((*it).materialize() == expected);
|
||||
CHECK(v.value(ptr, ordered_json(nullptr)) == expected);
|
||||
if (expected.is_string())
|
||||
{
|
||||
CHECK(v.value(key, std::string("-")) == expected.get<std::string>());
|
||||
}
|
||||
check_lookups(m);
|
||||
}
|
||||
CHECK(v["missing#key"].is_discarded());
|
||||
CHECK(!v.contains("missing#key"));
|
||||
CHECK(v.find("missing#key") == v.end());
|
||||
}
|
||||
else if (v.is_array())
|
||||
{
|
||||
for (std::size_t i = 0; i < v.size(); ++i)
|
||||
{
|
||||
check_lookups(v[i]);
|
||||
check_lookups(v.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// every lookup of the edited view finds what the first members of j hold
|
||||
void check_first_members(const ordered_json_editable_view& v, const ordered_json& j)
|
||||
{
|
||||
REQUIRE(v.type() == j.type());
|
||||
if (j.is_object())
|
||||
@@ -238,45 +285,39 @@ void check_lookups(const ordered_json_editable_view& v, const ordered_json& j)
|
||||
const std::string& key = kv.key();
|
||||
const ptr_t ptr = ptr_t() / key;
|
||||
CAPTURE(key)
|
||||
const ordered_json_editable_view m = v[key];
|
||||
REQUIRE(!m.is_discarded());
|
||||
CHECK(m.materialize() == kv.value());
|
||||
CHECK(v.at(key).materialize() == kv.value());
|
||||
CHECK(v[ptr].materialize() == kv.value());
|
||||
CHECK(v.at(ptr).materialize() == kv.value());
|
||||
CHECK(v.contains(key));
|
||||
REQUIRE(v.contains(key));
|
||||
CHECK(v.contains(ptr));
|
||||
CHECK(v.count(key) == 1);
|
||||
const auto it = v.find(key);
|
||||
REQUIRE(it != v.end());
|
||||
CHECK((*it).materialize() == kv.value());
|
||||
CHECK(v.value(ptr, ordered_json(nullptr)) == kv.value());
|
||||
if (kv.value().is_string())
|
||||
{
|
||||
CHECK(v.value(key, std::string("-")) == kv.value().get<std::string>());
|
||||
}
|
||||
check_lookups(m, kv.value());
|
||||
CHECK(v.find(key) != v.end());
|
||||
CHECK(v.at(ptr).type() == kv.value().type());
|
||||
check_first_members(v[key], kv.value());
|
||||
check_first_members(v.at(key), kv.value());
|
||||
check_first_members(v[ptr], kv.value());
|
||||
}
|
||||
CHECK(v["missing#key"].is_discarded());
|
||||
CHECK(!v.contains("missing#key"));
|
||||
CHECK(v.find("missing#key") == v.end());
|
||||
}
|
||||
else if (j.is_array())
|
||||
{
|
||||
REQUIRE(v.size() == j.size());
|
||||
for (std::size_t i = 0; i < j.size(); ++i)
|
||||
{
|
||||
check_lookups(v[i], j[i]);
|
||||
check_lookups(v.at(i), j[i]);
|
||||
check_first_members(v[i], j[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(v.materialize() == j);
|
||||
}
|
||||
}
|
||||
|
||||
// (for documents whose objects repeat keys: dump(), size(), and iteration
|
||||
// list all members, so only the lookups and materialize() are compared)
|
||||
// (for documents whose objects repeat keys: j holds the first members, the
|
||||
// lookups must find them; dump(), size(), and iteration list all members, but
|
||||
// materialize() keeps the last value of a key, as parse() does of the dump)
|
||||
void check_duplicates(const ordered_json_editable_document& d, const ordered_json& j)
|
||||
{
|
||||
CHECK(d.root().materialize() == j);
|
||||
check_lookups(d.root(), j);
|
||||
check_first_members(d.root(), j);
|
||||
check_lookups(d.root());
|
||||
CHECK(d.root().materialize() == ordered_json::parse(d.root().dump()));
|
||||
}
|
||||
|
||||
void check_all(const ordered_json_editable_document& d, const ordered_json& j, bool deep)
|
||||
@@ -304,17 +345,25 @@ TEST_CASE("json_view edits: differential")
|
||||
// materialize, and read back the same
|
||||
for (int n = 0; n < 250; ++n)
|
||||
{
|
||||
// the last 100 documents repeat keys in some of their objects
|
||||
// the last 100 documents repeat keys in some of their objects; j then
|
||||
// holds the first member of each key, which the edits address
|
||||
const bool duplicates = n >= 150;
|
||||
ordered_json j = random_value(0);
|
||||
if (r(4) == 0)
|
||||
{
|
||||
j = ordered_json::object({{"a", random_value(1)}, {"b", random_value(1)}});
|
||||
}
|
||||
if (duplicates)
|
||||
{
|
||||
j = ordered_json::parse(j.dump()); // (normalized as the document reads it)
|
||||
}
|
||||
const std::string text = duplicates ? text_with_duplicates(j) : j.dump(r(2) == 0 ? -1 : 2);
|
||||
CAPTURE(text)
|
||||
ordered_json_editable_document d = ordered_json_editable_document::parse(text);
|
||||
j = ordered_json::parse(text);
|
||||
if (!duplicates)
|
||||
{
|
||||
j = ordered_json::parse(text);
|
||||
}
|
||||
const ordered_json_document other = ordered_json_document::parse(random_value(0).dump());
|
||||
const int edits = 30;
|
||||
for (int e = 0; e < edits; ++e)
|
||||
@@ -624,7 +673,7 @@ TEST_CASE("json_view edits: views and values")
|
||||
SECTION("duplicate keys")
|
||||
{
|
||||
json_editable_document d = json_editable_document::parse(R"({"a": 1, "b": 2, "a": 3})");
|
||||
d.set(d.root(), "a", 4); // the last member is assigned (at the position of the first), the others dropped
|
||||
d.set(d.root(), "a", 4); // the first member is assigned, the others dropped
|
||||
CHECK(d.root().dump() == R"({"a":4,"b":2})");
|
||||
d = json_editable_document::parse(R"({"a": 1, "b": 2, "a": 3})");
|
||||
CHECK(d.erase(d.root(), "a") == 2);
|
||||
@@ -911,10 +960,10 @@ TEST_CASE("json_view edits: replacing arrays and objects by scalars")
|
||||
|
||||
namespace
|
||||
{
|
||||
// reads of "a" in an object that repeats it: the last member wins, however the
|
||||
// reads of "a" in an object that repeats it: the first member wins, however the
|
||||
// object is stored
|
||||
template<typename View>
|
||||
void check_last_wins(const View& o, int expected)
|
||||
void check_first_wins(const View& o, int expected)
|
||||
{
|
||||
CAPTURE(expected)
|
||||
CHECK(o["a"].template get<int>() == expected);
|
||||
@@ -935,35 +984,36 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
SECTION("an object in its parsed layout, then moved by edits")
|
||||
{
|
||||
json_editable_document d = json_editable_document::parse(R"({"a": 1, "b": 2, "a": 3})");
|
||||
check_last_wins(d.root(), 3);
|
||||
check_first_wins(d.root(), 1);
|
||||
CHECK(d.root().size() == 3);
|
||||
CHECK(d.root().dump() == R"({"a":1,"b":2,"a":3})");
|
||||
CHECK(d.root().materialize()["a"] == 3); // (parse() keeps the last value)
|
||||
|
||||
// an edit of a value does not move the object
|
||||
d.set(d.root()["b"], 5);
|
||||
check_last_wins(d.root(), 3);
|
||||
d.set(d.root()["a"], 4); // the member that reads find
|
||||
check_last_wins(d.root(), 4);
|
||||
CHECK(d.root().dump() == R"({"a":1,"b":5,"a":4})");
|
||||
check_first_wins(d.root(), 1);
|
||||
d.set(d.root()["a"], 4); // the member that lookups find
|
||||
check_first_wins(d.root(), 4);
|
||||
CHECK(d.root().dump() == R"({"a":4,"b":5,"a":3})");
|
||||
|
||||
// an appended member moves it
|
||||
d.set(d.root(), "c", true);
|
||||
check_last_wins(d.root(), 4);
|
||||
check_first_wins(d.root(), 4);
|
||||
CHECK(d.root().size() == 4);
|
||||
CHECK(d.root().dump() == R"({"a":1,"b":5,"a":4,"c":true})");
|
||||
CHECK(d.root().dump() == R"({"a":4,"b":5,"a":3,"c":true})");
|
||||
d.set(d.root()["a"], 6);
|
||||
check_last_wins(d.root(), 6);
|
||||
CHECK(d.root().dump() == R"({"a":1,"b":5,"a":6,"c":true})");
|
||||
check_first_wins(d.root(), 6);
|
||||
CHECK(d.root().dump() == R"({"a":6,"b":5,"a":3,"c":true})");
|
||||
d.set(json::json_pointer("/a"), 7);
|
||||
check_last_wins(d.root(), 7);
|
||||
check_first_wins(d.root(), 7);
|
||||
}
|
||||
|
||||
SECTION("set assigns the member that reads find")
|
||||
SECTION("set assigns the member that lookups find")
|
||||
{
|
||||
// the key keeps the position of its first occurrence (as in materialize()), the later members are dropped
|
||||
// the key stays where its first occurrence is, the later members are dropped
|
||||
ordered_json_editable_document d = ordered_json_editable_document::parse(R"({"a": 1, "b": 2, "a": 3, "c": 4, "a": 5})");
|
||||
const ordered_json_editable_view held = d.root()["a"];
|
||||
CHECK(held.get<int>() == 5);
|
||||
CHECK(held.get<int>() == 1);
|
||||
const ordered_json_editable_view assigned = d.set(d.root(), "a", "x");
|
||||
CHECK(held.get<std::string>() == "x");
|
||||
CHECK(assigned.get<std::string>() == "x");
|
||||
@@ -979,7 +1029,7 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
// through a pointer, below a duplicate
|
||||
d = ordered_json_editable_document::parse(R"({"a": {"x": 1}, "b": 2, "a": {"x": 3}})");
|
||||
d.set(ptr_t("/a/x"), 4);
|
||||
CHECK(d.root().dump() == R"({"a":{"x":1},"b":2,"a":{"x":4}})");
|
||||
CHECK(d.root().dump() == R"({"a":{"x":4},"b":2,"a":{"x":3}})");
|
||||
d.set(ptr_t("/a"), 0);
|
||||
CHECK(d.root().dump() == R"({"a":0,"b":2})");
|
||||
}
|
||||
@@ -988,7 +1038,7 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
{
|
||||
json_editable_document d = json_editable_document::parse(R"({"a": 1, "b": 2, "a": 3})");
|
||||
d.set(d.root(), "c", 4);
|
||||
check_last_wins(d.root(), 3);
|
||||
check_first_wins(d.root(), 1);
|
||||
CHECK(d.erase(d.root(), "a") == 2);
|
||||
CHECK(d.root().dump() == R"({"b":2,"c":4})");
|
||||
CHECK(!d.root().contains("a"));
|
||||
@@ -1008,20 +1058,20 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
}
|
||||
text += R"(,"a":1,"k7":"last","a":2})";
|
||||
json_editable_document d = json_editable_document::parse(text);
|
||||
check_last_wins(d.root(), 2);
|
||||
CHECK(d.root()["k7"].get_string() == "last");
|
||||
check_first_wins(d.root(), 0);
|
||||
CHECK(d.root()["k7"].get_string() == "first");
|
||||
CHECK(d.root()["k199"].get<int>() == 199);
|
||||
|
||||
// a value assigned in place: the index stays in use
|
||||
d.set(d.root()["a"], 3);
|
||||
check_last_wins(d.root(), 3);
|
||||
check_first_wins(d.root(), 3);
|
||||
d.set(d.root()["k7"], "changed");
|
||||
CHECK(d.root()["k7"].get_string() == "changed");
|
||||
CHECK(d.root()["k199"].get<int>() == 199);
|
||||
|
||||
// an appended member moves the members: the lookup scans them
|
||||
d.set(d.root(), "new", 1);
|
||||
check_last_wins(d.root(), 3);
|
||||
check_first_wins(d.root(), 3);
|
||||
CHECK(d.root()["k7"].get_string() == "changed");
|
||||
CHECK(d.root()["k7"].get_string() == d.root().at("k7").get_string());
|
||||
CHECK(d.root()["k199"].get<int>() == 199);
|
||||
@@ -1031,7 +1081,7 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
d = json_editable_document::parse(text);
|
||||
d.set(d.root()["a"], json{{"x", 1}});
|
||||
CHECK(d.root()["a"]["x"].get<int>() == 1);
|
||||
CHECK(d.root()["k7"].get_string() == "last");
|
||||
CHECK(d.root()["k7"].get_string() == "first");
|
||||
CHECK(d.erase(d.root(), "k7") == 3); // (the key occurs three times)
|
||||
CHECK(d.root()["k7"].is_discarded());
|
||||
CHECK(d.root()["a"]["x"].get<int>() == 1);
|
||||
@@ -1040,18 +1090,18 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
SECTION("objects in moved arrays and in new values")
|
||||
{
|
||||
json_editable_document d = json_editable_document::parse(R"([{"a": 1, "a": 2}, {"a": 3, "b": 0, "a": 4}])");
|
||||
check_last_wins(d.root()[0], 2);
|
||||
check_last_wins(d.root()[1], 4);
|
||||
check_first_wins(d.root()[0], 1);
|
||||
check_first_wins(d.root()[1], 3);
|
||||
d.insert(d.root(), 0, json::parse(R"({"a": 0})"));
|
||||
d.push_back(d.root(), json::parse(R"({"a": 5, "a": 6})")); // (a basic_json value has one member)
|
||||
check_last_wins(d.root()[1], 2);
|
||||
check_last_wins(d.root()[2], 4);
|
||||
check_first_wins(d.root()[1], 1);
|
||||
check_first_wins(d.root()[2], 3);
|
||||
CHECK(d.root()[3]["a"].get<int>() == 6);
|
||||
d.set(d.root()[1], "a", 8);
|
||||
check_last_wins(d.root()[1], 8);
|
||||
check_first_wins(d.root()[1], 8);
|
||||
CHECK(d.root()[1].dump() == R"({"a":8})");
|
||||
d.erase(d.root(), 0);
|
||||
check_last_wins(d.root()[1], 4);
|
||||
check_first_wins(d.root()[1], 3);
|
||||
CHECK(d.root().dump() == R"([{"a":8},{"a":3,"b":0,"a":4},{"a":6}])");
|
||||
}
|
||||
|
||||
@@ -1063,14 +1113,14 @@ TEST_CASE("json_view edits: duplicate keys")
|
||||
d.set(d.root(), "o", source.root()["o"]);
|
||||
// (the copies keep the repeated members)
|
||||
CHECK(d.root().dump() == R"({"copy":[{"a":1,"a":2}],"o":{"b":{"a":3,"a":4},"a":5,"a":6}})");
|
||||
check_last_wins(d.root()["copy"][0], 2);
|
||||
check_last_wins(d.root()["o"]["b"], 4);
|
||||
check_last_wins(d.root()["o"], 6);
|
||||
check_first_wins(d.root()["copy"][0], 1);
|
||||
check_first_wins(d.root()["o"]["b"], 3);
|
||||
check_first_wins(d.root()["o"], 5);
|
||||
d.set(d.root()["o"]["b"], "c", 0);
|
||||
check_last_wins(d.root()["o"]["b"], 4);
|
||||
check_first_wins(d.root()["o"]["b"], 3);
|
||||
d.set(d.root()["o"], "d", 0);
|
||||
check_last_wins(d.root()["o"], 6);
|
||||
CHECK(d.root()["o"]["b"]["a"].get<int>() == 4);
|
||||
check_first_wins(d.root()["o"], 5);
|
||||
CHECK(d.root()["o"]["b"]["a"].get<int>() == 3);
|
||||
CHECK(d.root()["o"]["d"].get<int>() == 0);
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ TEST_CASE("json_view images: round trips")
|
||||
{
|
||||
CHECK(l.root()["k" + std::to_string(i)] == d.root()["k" + std::to_string(i)]);
|
||||
}
|
||||
CHECK(l.root()["k7"] == "a duplicate"); // the last of duplicate keys
|
||||
CHECK(l.root()["k7"].get<int>() == 7); // the first of duplicate keys
|
||||
CHECK(l.root()["inner"]["m199"].get<int>() == -199);
|
||||
CHECK(!l.root().contains("k1000"));
|
||||
// the index is not part of the image
|
||||
@@ -251,18 +251,18 @@ TEST_CASE("json_view images: round trips")
|
||||
CHECK(node_at(image, 0).extra == 0);
|
||||
}
|
||||
|
||||
SECTION("duplicate keys of a large object: lookups return the last member")
|
||||
SECTION("duplicate keys of a large object: lookups return the first member")
|
||||
{
|
||||
std::string text = "{";
|
||||
for (int i = 0; i < 200; ++i)
|
||||
{
|
||||
text += (i != 0 ? ",\"k" : "\"k") + std::to_string(i) + "\":" + std::to_string(i);
|
||||
}
|
||||
// three members for k5 (the third is the last), and duplicates of k0 and k199
|
||||
// three members for k5 (the first is a number), and duplicates of k0 and k199
|
||||
text += R"(,"k5":"two","k0":null,"k199":[],"k5":"three"})";
|
||||
const json_document d = json_document::parse(text);
|
||||
REQUIRE(d.root().size() == 204);
|
||||
CHECK(d.root()["k5"] == "three");
|
||||
CHECK(d.root()["k5"] == 5);
|
||||
const std::vector<std::uint8_t> image = d.save();
|
||||
for (const image_check check :
|
||||
{
|
||||
@@ -271,13 +271,15 @@ TEST_CASE("json_view images: round trips")
|
||||
{
|
||||
const json_document l = json_document::load(image, check);
|
||||
CHECK(l.root().size() == 204);
|
||||
CHECK(l.root()["k5"] == "three");
|
||||
CHECK(l.root().at("k5") == "three");
|
||||
CHECK(l.root().find("k5").value() == "three");
|
||||
CHECK(l.root()["k0"].is_null());
|
||||
CHECK(l.root()["k199"] == json::array());
|
||||
CHECK(l.root()["k5"] == 5);
|
||||
CHECK(l.root().at("k5") == 5);
|
||||
CHECK(l.root().find("k5").value() == 5);
|
||||
CHECK(l.root()["k0"] == 0);
|
||||
CHECK(l.root()["k199"] == 199);
|
||||
CHECK(l.root()["k100"] == 100);
|
||||
// materialize() keeps the semantics of parse(): the last value
|
||||
CHECK(l.root().materialize() == d.root().materialize());
|
||||
CHECK(l.root().materialize()["k5"] == "three");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,15 +357,8 @@ TEST_CASE("json_view images: round trips")
|
||||
for (std::size_t i = 0; i < members; ++i)
|
||||
{
|
||||
CAPTURE(i)
|
||||
const bool duplicate = std::find(duplicated.begin(), duplicated.end(), keys[i]) != duplicated.end();
|
||||
if (duplicate)
|
||||
{
|
||||
CHECK(l.root()[keys[i]] == "last");
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(l.root()[keys[i]] == i);
|
||||
}
|
||||
// lookups find the first member, also of the duplicated keys
|
||||
CHECK(l.root()[keys[i]] == i);
|
||||
CHECK(l.root().at(keys[i]) == l.root()[keys[i]]);
|
||||
CHECK(l.root().find(keys[i]).key() == keys[i]);
|
||||
CHECK(!l.root().contains(keys[i] + "x"));
|
||||
|
||||
Reference in new issue
Block a user