mirror of
https://github.com/nlohmann/json.git
synced 2026-10-01 13:34:14 +07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
286294739a | ||
|
|
6a073dbae4 | ||
|
|
fdcc569eee | ||
|
|
1daad8efe0 | ||
|
|
989b8ed841 |
@@ -69,3 +69,5 @@ 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`.
|
||||
|
||||
@@ -18,6 +18,10 @@ Deserializes an input stream to a JSON value.
|
||||
|
||||
the stream `i`
|
||||
|
||||
## Exception safety
|
||||
|
||||
Strong guarantee: if an exception is thrown, there are no changes in `j`.
|
||||
|
||||
## Exceptions
|
||||
|
||||
- Throws [`parse_error.101`](../home/exceptions.md#jsonexceptionparse_error101) in case of an unexpected token, or if
|
||||
@@ -125,3 +129,5 @@ being read.
|
||||
the stream; planned to become the default in version 4.0.0.
|
||||
- Fixed a null pointer dereference for an `std::istream` without a stream buffer (now throws `parse_error.101`), and a
|
||||
crash (`std::terminate`) when `i` has `eofbit` in its exception mask, in version 3.13.0.
|
||||
- Changed to the strong exception safety guarantee in version 3.13.0: `j` is no longer left with a partially parsed
|
||||
value if parsing throws.
|
||||
|
||||
@@ -389,6 +389,7 @@ using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;
|
||||
| Functionality | Additional requirement |
|
||||
|-----------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [`diff`](../../api/basic_json/diff.md), [`items`](../../api/basic_json/items.md), [`std::hash`](../../api/basic_json/std_hash.md) | conversion of a `#!cpp std::size_t` to `StringType`: either assignability from the result of `#!cpp std::to_string`, or an ADL overload `#!cpp void int_to_string(StringType&, std::size_t)` |
|
||||
| [`operator/(std::size_t)`](../../api/json_pointer/operator_slash.md) | the same conversion of a `#!cpp std::size_t` to `StringType` as `diff`, `items`, and `std::hash` above |
|
||||
| [`std::hash<basic_json>`](../../api/basic_json/std_hash.md) | additionally a specialization of `#!cpp std::hash<StringType>` |
|
||||
| [`to_bson`](../../api/basic_json/to_bson.md) | `find(value_type)` and `npos` |
|
||||
| [`parse`](../../api/basic_json/parse.md) from a `string_t` | the input adapters must accept it; otherwise pass a character range |
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <nlohmann/detail/macro_scope.hpp>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <nlohmann/detail/string_escape.hpp>
|
||||
#include <nlohmann/detail/string_utils.hpp>
|
||||
#include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
@@ -116,7 +117,7 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(std::size_t array_idx)
|
||||
{
|
||||
return *this /= std::to_string(array_idx);
|
||||
return *this /= detail::to_string<string_t>(array_idx);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||
@@ -752,7 +753,7 @@ class json_pointer
|
||||
// would throw out_of_range.404 -- contains() must not throw (see #5395)
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
@@ -780,7 +781,7 @@ class json_pointer
|
||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
||||
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
{
|
||||
|
||||
@@ -5092,7 +5092,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
|
||||
friend std::istream& operator>>(std::istream& i, basic_json& j)
|
||||
{
|
||||
parser(detail::input_adapter(i)).parse(false, j);
|
||||
// parse into a temporary so that j is left unchanged if parsing fails
|
||||
basic_json result;
|
||||
parser(detail::input_adapter(i)).parse(false, result);
|
||||
j = std::move(result);
|
||||
return i;
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
@@ -72,7 +72,9 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
|
||||
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)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -81,13 +83,14 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
append(key, std::forward<T>(t));
|
||||
append(key, std::forward<V>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
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)
|
||||
template<class KeyType, class V, detail::enable_if_t<
|
||||
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
|
||||
detail::is_constructible<T, V>>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(KeyType && key, V && t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -96,7 +99,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
append(std::forward<KeyType>(key), std::forward<T>(t));
|
||||
append(std::forward<KeyType>(key), std::forward<V>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
|
||||
@@ -19636,6 +19636,8 @@ NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
// #include <nlohmann/detail/string_escape.hpp>
|
||||
|
||||
// #include <nlohmann/detail/string_utils.hpp>
|
||||
|
||||
// #include <nlohmann/detail/value_t.hpp>
|
||||
|
||||
|
||||
@@ -19727,7 +19729,7 @@ class json_pointer
|
||||
/// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/
|
||||
json_pointer& operator/=(std::size_t array_idx)
|
||||
{
|
||||
return *this /= std::to_string(array_idx);
|
||||
return *this /= detail::to_string<string_t>(array_idx);
|
||||
}
|
||||
|
||||
/// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer
|
||||
@@ -20363,7 +20365,7 @@ class json_pointer
|
||||
// would throw out_of_range.404 -- contains() must not throw (see #5395)
|
||||
return false;
|
||||
}
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9")))
|
||||
if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !('0' <= reference_token[0] && reference_token[0] <= '9')))
|
||||
{
|
||||
// invalid char
|
||||
return false;
|
||||
@@ -20391,7 +20393,7 @@ class json_pointer
|
||||
// not throw (see #5395), so such a reference token is treated as "not found"
|
||||
errno = 0; // strtoull() does not reset errno on success
|
||||
char* p_end = nullptr; // NOLINT(misc-const-correctness)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.c_str(), &p_end, 10); // NOLINT(runtime/int)
|
||||
const unsigned long long magnitude = std::strtoull(reference_token.data(), &p_end, 10); // NOLINT(runtime/int)
|
||||
if (JSON_HEDLEY_UNLIKELY(errno == ERANGE // the value exceeds ULLONG_MAX
|
||||
|| magnitude >= static_cast<unsigned long long>((std::numeric_limits<typename BasicJsonType::size_type>::max)()))) // NOLINT(runtime/int)
|
||||
{
|
||||
@@ -26620,7 +26622,9 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::pair<iterator, bool> emplace(const key_type& key, T&& t)
|
||||
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)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -26629,13 +26633,14 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
append(key, std::forward<T>(t));
|
||||
append(key, std::forward<V>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
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)
|
||||
template<class KeyType, class V, detail::enable_if_t<
|
||||
detail::conjunction<detail::is_usable_as_key_type<key_compare, key_type, KeyType>,
|
||||
detail::is_constructible<T, V>>::value, int> = 0>
|
||||
std::pair<iterator, bool> emplace(KeyType && key, V && t)
|
||||
{
|
||||
for (auto it = this->begin(); it != this->end(); ++it)
|
||||
{
|
||||
@@ -26644,7 +26649,7 @@ template <class Key, class T, class IgnoredLess = std::less<Key>,
|
||||
return {it, false};
|
||||
}
|
||||
}
|
||||
append(std::forward<KeyType>(key), std::forward<T>(t));
|
||||
append(std::forward<KeyType>(key), std::forward<V>(t));
|
||||
return {std::prev(this->end()), true};
|
||||
}
|
||||
|
||||
@@ -32017,7 +32022,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
|
||||
/// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/
|
||||
friend std::istream& operator>>(std::istream& i, basic_json& j)
|
||||
{
|
||||
parser(detail::input_adapter(i)).parse(false, j);
|
||||
// parse into a temporary so that j is left unchanged if parsing fails
|
||||
basic_json result;
|
||||
parser(detail::input_adapter(i)).parse(false, result);
|
||||
j = std::move(result);
|
||||
return i;
|
||||
}
|
||||
#endif // JSON_NO_IO
|
||||
|
||||
@@ -352,6 +352,41 @@ TEST_CASE("alternative string type")
|
||||
CHECK(j2.flatten().unflatten() == j2);
|
||||
}
|
||||
|
||||
SECTION("contains(json_pointer)")
|
||||
{
|
||||
// contains(json_pointer) must compile and work with a string_t that has
|
||||
// no c_str() and no comparison with const char* (see #5666)
|
||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||
|
||||
// present: object key and array indices
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo")));
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo/0")));
|
||||
CHECK(j.contains(alt_json::json_pointer("/foo/1")));
|
||||
|
||||
// missing: absent object key and out-of-range array index
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/bar")));
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/2")));
|
||||
|
||||
// "-" always fails the range check
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/-")));
|
||||
|
||||
// an array index must not have a leading zero
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/01")));
|
||||
|
||||
// a reference token that is not a number
|
||||
CHECK_FALSE(j.contains(alt_json::json_pointer("/foo/bar")));
|
||||
}
|
||||
|
||||
SECTION("operator/(std::size_t)")
|
||||
{
|
||||
// json_pointer::operator/=(std::size_t) must compile without string_t
|
||||
// being constructible from std::string (see #5666)
|
||||
auto j = alt_json::parse(R"({"foo": ["bar", "baz"]})");
|
||||
|
||||
CHECK(j.at(alt_json::json_pointer("/foo") / std::size_t(0)) == j["foo"][0]);
|
||||
CHECK(j.at(alt_json::json_pointer("/foo") / std::size_t(1)) == j["foo"][1]);
|
||||
}
|
||||
|
||||
SECTION("patch")
|
||||
{
|
||||
alt_json const patch1 = alt_json::parse(R"([{ "op": "add", "path": "/a/b", "value": [ "foo", "bar" ] }])");
|
||||
|
||||
@@ -19,6 +19,7 @@ using nlohmann::json;
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
|
||||
TEST_CASE("Better diagnostics")
|
||||
{
|
||||
@@ -492,6 +493,21 @@ TEST_CASE("Regression tests for extended diagnostics")
|
||||
CHECK(copy == j);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("Regression test for issue #5652 - operator>> leaves a partial value in its target on a parse error")
|
||||
{
|
||||
json j = "old value";
|
||||
std::istringstream is("[1, x");
|
||||
CHECK_THROWS_WITH_AS(is >> j, "[json.exception.parse_error.101] parse error at line 1, column 5: syntax error while parsing value - invalid literal; last read: '1, x'", json::parse_error);
|
||||
|
||||
// j must be left unchanged, as json::parse() guarantees for its result
|
||||
CHECK(j == "old value");
|
||||
|
||||
// copying j must not trigger assert_invariant(): a failed parse must
|
||||
// not leave array/object elements without a parent pointer
|
||||
json const copy = j; // NOLINT(performance-unnecessary-copy-initialization)
|
||||
CHECK(copy == j);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Better diagnostics past the descent bound of update() and merge_patch()")
|
||||
|
||||
@@ -196,3 +196,44 @@ 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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,6 +403,79 @@ 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ordered_map growth")
|
||||
|
||||
Reference in New Issue
Block a user