mirror of
https://github.com/nlohmann/json.git
synced 2026-09-30 03:30:31 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9802542843 |
@@ -69,5 +69,3 @@ Logarithmic in the size of the container, O(log(`size()`)).
|
||||
## Version history
|
||||
|
||||
- Since version 2.0.8.
|
||||
- Fixed in version 3.13.0: for [`ordered_json`](../ordered_json.md), the value could previously only be passed as an
|
||||
rvalue; it can now also be passed as an lvalue or a `#!cpp const` lvalue, matching the behavior of `json`.
|
||||
|
||||
@@ -6187,7 +6187,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
}
|
||||
|
||||
if (common_keys_source_order == common_keys_target_order && new_keys_form_suffix)
|
||||
// Only an object type that keeps its members in insertion
|
||||
// order, such as nlohmann::ordered_map, can need reordering:
|
||||
// patch() appends a new member at the end of such an object.
|
||||
// Any other object type places its members itself - std::map
|
||||
// in key order, a hash map in an order its operator== ignores -
|
||||
// so a member-by-member diff always reproduces target there.
|
||||
if (!detail::is_ordered_map<object_t>::value
|
||||
|| (common_keys_source_order == common_keys_target_order && new_keys_form_suffix))
|
||||
{
|
||||
// fast path: order of common keys already matches (or the
|
||||
// object_t's iteration order does not depend on
|
||||
|
||||
@@ -70,9 +70,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return *this;
|
||||
}
|
||||
|
||||
template < class V, detail::enable_if_t <
|
||||
detail::is_constructible < T, V&& >::value, int > = 0 >
|
||||
std::pair<iterator, bool> emplace(const key_type& key, V && t)
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -81,14 +79,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(key, std::forward<V>(t));
|
||||
Container::emplace_back(key, std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
template < class KeyType, class V, detail::enable_if_t <
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value&&
|
||||
detail::is_constructible < T, V&& >::value, int > = 0 >
|
||||
std::pair<iterator, bool> emplace(KeyType && key, V && t)
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(KeyType && key, T && t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -97,7 +94,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<V>(t));
|
||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
|
||||
@@ -25832,9 +25832,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return *this;
|
||||
}
|
||||
|
||||
template < class V, detail::enable_if_t <
|
||||
detail::is_constructible < T, V&& >::value, int > = 0 >
|
||||
std::pair<iterator, bool> emplace(const key_type& key, V && t)
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -25843,14 +25841,13 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(key, std::forward<V>(t));
|
||||
Container::emplace_back(key, std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
template < class KeyType, class V, detail::enable_if_t <
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value&&
|
||||
detail::is_constructible < T, V&& >::value, int > = 0 >
|
||||
std::pair<iterator, bool> emplace(KeyType && key, V && t)
|
||||
template<class KeyType, detail::enable_if_t<
|
||||
detail::is_usable_as_key_type<key_compare, key_type, KeyType>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(KeyType && key, T && t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -25859,7 +25856,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<V>(t));
|
||||
Container::emplace_back(std::forward<KeyType>(key), std::forward<T>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
@@ -32271,7 +32268,14 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
}
|
||||
}
|
||||
|
||||
if (common_keys_source_order == common_keys_target_order && new_keys_form_suffix)
|
||||
// Only an object type that keeps its members in insertion
|
||||
// order, such as nlohmann::ordered_map, can need reordering:
|
||||
// patch() appends a new member at the end of such an object.
|
||||
// Any other object type places its members itself - std::map
|
||||
// in key order, a hash map in an order its operator== ignores -
|
||||
// so a member-by-member diff always reproduces target there.
|
||||
if (!detail::is_ordered_map<object_t>::value
|
||||
|| (common_keys_source_order == common_keys_target_order && new_keys_form_suffix))
|
||||
{
|
||||
// fast path: order of common keys already matches (or the
|
||||
// object_t's iteration order does not depend on
|
||||
|
||||
@@ -1752,6 +1752,58 @@ TEST_CASE("JSON patch - diff emits array removals in descending index order")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JSON patch - diff() takes the fast path for non-reorderable object types (regression #5639)")
|
||||
{
|
||||
// #5465 added an order check to diff()'s object handling so a
|
||||
// member-by-member diff is only used when it would also reproduce
|
||||
// target's member *order* -- needed for ordered_json, whose object_t
|
||||
// keeps insertion order and whose patch() "add" op appends a new
|
||||
// member at the end. For json's default object_t (std::map, which
|
||||
// orders members by key regardless of insertion history), that check
|
||||
// could still fail: a new key that sorts before an existing common key
|
||||
// makes target's iteration interleave the new key between common keys,
|
||||
// even though nothing else about the object changed. That sent the
|
||||
// whole object through the slow (remove-every-member,
|
||||
// re-add-every-member) path instead of the minimal one.
|
||||
SECTION("json: added key sorts before an existing common key")
|
||||
{
|
||||
const json source = {{"a", 1}, {"c", {{"x", 1}, {"y", 2}}}};
|
||||
const json target = {{"a", 1}, {"b", 0}, {"c", {{"x", 1}, {"y", 2}}}};
|
||||
|
||||
const json patch = json::diff(source, target);
|
||||
|
||||
// only the new key is added; "a" and "c" are left alone instead of
|
||||
// being removed and re-added
|
||||
const json expected = R"([{"op": "add", "path": "/b", "value": 0}])"_json;
|
||||
CHECK(patch == expected);
|
||||
CHECK(source.patch(patch) == target);
|
||||
}
|
||||
|
||||
SECTION("ordered_json: reordering behavior from #5465 is unchanged")
|
||||
{
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
// same key/value shape as the json case above, but for ordered_json
|
||||
// the *target*'s member order must be reproduced, so the slow path
|
||||
// is still required here.
|
||||
ordered_json source;
|
||||
source["a"] = 1;
|
||||
source["c"] = ordered_json{{"x", 1}, {"y", 2}};
|
||||
|
||||
ordered_json target;
|
||||
target["a"] = 1;
|
||||
target["b"] = 0;
|
||||
target["c"] = ordered_json{{"x", 1}, {"y", 2}};
|
||||
|
||||
const ordered_json patch = ordered_json::diff(source, target);
|
||||
|
||||
// unlike the json case: every member is still removed and re-added
|
||||
// so the result ends up in target's order (2 removes + 3 adds)
|
||||
CHECK(patch.size() == 5);
|
||||
CHECK(source.patch(patch) == target);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("JSON patch - every operation on ordered_json")
|
||||
{
|
||||
using nlohmann::ordered_json;
|
||||
|
||||
@@ -196,44 +196,3 @@ TEST_CASE("regression test - diff() must account for ordered_json member order")
|
||||
CHECK(a.patch(p) == b);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("regression test for issue #5673 - ordered_json::emplace with a non-rvalue value")
|
||||
{
|
||||
SECTION("lvalue value")
|
||||
{
|
||||
ordered_json oj = ordered_json::object();
|
||||
ordered_json value = 1;
|
||||
auto res = oj.emplace("a", value);
|
||||
CHECK(res.second == true);
|
||||
CHECK(oj.dump() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
SECTION("const lvalue value")
|
||||
{
|
||||
ordered_json oj = ordered_json::object();
|
||||
const ordered_json value = 1;
|
||||
auto res = oj.emplace("a", value);
|
||||
CHECK(res.second == true);
|
||||
CHECK(oj.dump() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
SECTION("rvalue value")
|
||||
{
|
||||
ordered_json oj = ordered_json::object();
|
||||
auto res = oj.emplace("a", ordered_json(1));
|
||||
CHECK(res.second == true);
|
||||
CHECK(oj.dump() == "{\"a\":1}");
|
||||
}
|
||||
|
||||
SECTION("existing key is not overwritten (std::map-compatible semantics)")
|
||||
{
|
||||
ordered_json oj = ordered_json::object();
|
||||
ordered_json value = 1;
|
||||
oj.emplace("a", value);
|
||||
|
||||
ordered_json other_value = 2;
|
||||
auto res = oj.emplace("a", other_value);
|
||||
CHECK(res.second == false);
|
||||
CHECK(oj.dump() == "{\"a\":1}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,77 +312,4 @@ TEST_CASE("ordered_map")
|
||||
CHECK(om.size() == 4);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("emplace")
|
||||
{
|
||||
// regression test for issue #5673: the mapped-value parameter must
|
||||
// accept lvalues and const lvalues, not just rvalues
|
||||
ordered_map<std::string, std::string> om;
|
||||
om["eins"] = "one";
|
||||
om["zwei"] = "two";
|
||||
om["drei"] = "three";
|
||||
|
||||
SECTION("with T&& (rvalue)")
|
||||
{
|
||||
auto res1 = om.emplace("eins", std::string("1"));
|
||||
CHECK(res1.first == om.begin());
|
||||
CHECK(res1.second == false);
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.at("eins") == "one"); // existing key is not overwritten
|
||||
|
||||
auto res4 = om.emplace("vier", std::string("four"));
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
CHECK(om.at("vier") == "four");
|
||||
}
|
||||
|
||||
SECTION("with T& (lvalue)")
|
||||
{
|
||||
std::string one = "1";
|
||||
std::string four = "four";
|
||||
|
||||
auto res1 = om.emplace("eins", one);
|
||||
CHECK(res1.first == om.begin());
|
||||
CHECK(res1.second == false);
|
||||
CHECK(om.size() == 3);
|
||||
CHECK(om.at("eins") == "one"); // existing key is not overwritten
|
||||
|
||||
auto res4 = om.emplace("vier", four);
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
CHECK(om.at("vier") == "four");
|
||||
CHECK(four == "four"); // source was copied, not moved from
|
||||
}
|
||||
|
||||
SECTION("with const T&")
|
||||
{
|
||||
const std::string one = "1";
|
||||
const std::string four = "four";
|
||||
|
||||
auto res1 = om.emplace("eins", one);
|
||||
CHECK(res1.first == om.begin());
|
||||
CHECK(res1.second == false);
|
||||
CHECK(om.size() == 3);
|
||||
|
||||
auto res4 = om.emplace("vier", four);
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
CHECK(om.at("vier") == "four");
|
||||
}
|
||||
|
||||
SECTION("with key of key_type (non-template overload)")
|
||||
{
|
||||
const std::string key_vier{"vier"};
|
||||
std::string four = "four";
|
||||
|
||||
auto res4 = om.emplace(key_vier, four);
|
||||
CHECK(res4.first == om.begin() + 3);
|
||||
CHECK(res4.second == true);
|
||||
CHECK(om.size() == 4);
|
||||
CHECK(om.at("vier") == "four");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user