mirror of
https://github.com/nlohmann/json.git
synced 2026-10-10 08:27:13 +00:00
Return the first member of duplicate keys from lookups again
Looking up the last member of a duplicate key cannot stop at a match, so every lookup scanned the whole object (1.6 to 3.4 times slower for small objects). operator[](key), at, find, value, contains, count, and JSON pointer resolution return the first member again, as yyjson and simdjson do; materialize() and get<map>() keep the last value, as parse(). The documentation says so in the feature page and on each lookup page, and explains how to get the value parse() would give. The integer index templates and the discarded chaining of operator[] stay. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
15 files changed
+153
-80
No files matched your search
@@ -576,7 +576,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());
|
||||
@@ -617,23 +617,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());
|
||||
@@ -724,20 +714,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 :
|
||||
{
|
||||
@@ -747,10 +745,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;
|
||||
|
||||
Reference in new issue
Block a user