mirror of
https://github.com/nlohmann/json.git
synced 2026-11-02 10:55:35 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cdd9d576a2 | ||
|
|
ae792a4c95 |
No files matched your search
@@ -35,6 +35,9 @@ Note that enabling this macro increases the size of every JSON value by two `std
|
|||||||
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
overhead to parsing, copying JSON value objects, and the generation of error messages for exceptions. It also causes
|
||||||
these values to be reported in those error messages.
|
these values to be reported in those error messages.
|
||||||
|
|
||||||
|
If [`JSON_DIAGNOSTICS`](json_diagnostics.md) is also set, the message contains the path and the byte range, such as
|
||||||
|
`(/1) (bytes 4-8)`. An exception about the root value has no path and only the range, such as `(bytes 0-4)`.
|
||||||
|
|
||||||
## Default definition
|
## Default definition
|
||||||
|
|
||||||
The default value is `0` (position diagnostics are switched off).
|
The default value is `0` (position diagnostics are switched off).
|
||||||
|
|||||||
@@ -198,18 +198,19 @@ class exception : public std::exception
|
|||||||
// all other parent types (primitives) contribute no token
|
// all other parent types (primitives) contribute no token
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.empty())
|
// the root value has no path, but still has a byte range
|
||||||
|
std::string prefix;
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
return "";
|
const auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
||||||
}
|
|
||||||
|
|
||||||
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
|
||||||
[](const std::string & a, const std::string & b)
|
[](const std::string & a, const std::string & b)
|
||||||
{
|
{
|
||||||
return concat(a, '/', detail::escape(b));
|
return concat(a, '/', detail::escape(b));
|
||||||
});
|
});
|
||||||
|
prefix = concat('(', str, ") ");
|
||||||
|
}
|
||||||
|
|
||||||
return concat('(', str, ") ", get_byte_positions(leaf_element));
|
return concat(prefix, get_byte_positions(leaf_element));
|
||||||
#else
|
#else
|
||||||
return get_byte_positions(leaf_element);
|
return get_byte_positions(leaf_element);
|
||||||
#endif
|
#endif
|
||||||
@@ -222,7 +223,8 @@ class exception : public std::exception
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
||||||
{
|
{
|
||||||
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
|
// a value-initialized iterator has no value (invalid_iterator.212)
|
||||||
|
if (leaf_element != nullptr && leaf_element->start_pos() != std::string::npos && leaf_element->end_pos() != std::string::npos)
|
||||||
{
|
{
|
||||||
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,8 +234,7 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std:
|
|||||||
|
|
||||||
/// the context basic_json puts in front of the message of an exception about
|
/// the context basic_json puts in front of the message of an exception about
|
||||||
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
||||||
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (with JSON_DIAGNOSTICS, only
|
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root)
|
||||||
/// if there is a path)
|
|
||||||
///
|
///
|
||||||
/// This and the throw helpers that take an error_site are templates, so that
|
/// This and the throw helpers that take an error_site are templates, so that
|
||||||
/// the path and range code is instantiated only where an exception can be
|
/// the path and range code is instantiated only where an exception can be
|
||||||
@@ -256,12 +255,11 @@ std::string diagnostics(const error_site site)
|
|||||||
std::string context;
|
std::string context;
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
const bool found = edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens);
|
// a value that is not found leaves no tokens, like the root
|
||||||
if (!found || tokens.empty())
|
static_cast<void>(edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens));
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
// context is still empty; returning it keeps the copy elided (-Wnrvo)
|
// the root value has no path, but still has a byte range
|
||||||
return context;
|
|
||||||
}
|
|
||||||
std::string path;
|
std::string path;
|
||||||
for (const std::string& token : tokens)
|
for (const std::string& token : tokens)
|
||||||
{
|
{
|
||||||
@@ -269,6 +267,7 @@ std::string diagnostics(const error_site site)
|
|||||||
path += detail::escape(token);
|
path += detail::escape(token);
|
||||||
}
|
}
|
||||||
context = concat('(', path, ") ");
|
context = concat('(', path, ") ");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
|
|||||||
Generated
+9
-7
@@ -5695,18 +5695,19 @@ class exception : public std::exception
|
|||||||
// all other parent types (primitives) contribute no token
|
// all other parent types (primitives) contribute no token
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.empty())
|
// the root value has no path, but still has a byte range
|
||||||
|
std::string prefix;
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
return "";
|
const auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
||||||
}
|
|
||||||
|
|
||||||
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
|
||||||
[](const std::string & a, const std::string & b)
|
[](const std::string & a, const std::string & b)
|
||||||
{
|
{
|
||||||
return concat(a, '/', detail::escape(b));
|
return concat(a, '/', detail::escape(b));
|
||||||
});
|
});
|
||||||
|
prefix = concat('(', str, ") ");
|
||||||
|
}
|
||||||
|
|
||||||
return concat('(', str, ") ", get_byte_positions(leaf_element));
|
return concat(prefix, get_byte_positions(leaf_element));
|
||||||
#else
|
#else
|
||||||
return get_byte_positions(leaf_element);
|
return get_byte_positions(leaf_element);
|
||||||
#endif
|
#endif
|
||||||
@@ -5719,7 +5720,8 @@ class exception : public std::exception
|
|||||||
template<typename BasicJsonType>
|
template<typename BasicJsonType>
|
||||||
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
||||||
{
|
{
|
||||||
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
|
// a value-initialized iterator has no value (invalid_iterator.212)
|
||||||
|
if (leaf_element != nullptr && leaf_element->start_pos() != std::string::npos && leaf_element->end_pos() != std::string::npos)
|
||||||
{
|
{
|
||||||
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+6
-7
@@ -5076,8 +5076,7 @@ void value_range(const document_data& d, const node* n, std::size_t& start, std:
|
|||||||
|
|
||||||
/// the context basic_json puts in front of the message of an exception about
|
/// the context basic_json puts in front of the message of an exception about
|
||||||
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
/// a value: "(/path) " with JSON_DIAGNOSTICS (none for the root), then
|
||||||
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (with JSON_DIAGNOSTICS, only
|
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (also for the root)
|
||||||
/// if there is a path)
|
|
||||||
///
|
///
|
||||||
/// This and the throw helpers that take an error_site are templates, so that
|
/// This and the throw helpers that take an error_site are templates, so that
|
||||||
/// the path and range code is instantiated only where an exception can be
|
/// the path and range code is instantiated only where an exception can be
|
||||||
@@ -5098,12 +5097,11 @@ std::string diagnostics(const error_site site)
|
|||||||
std::string context;
|
std::string context;
|
||||||
#if JSON_DIAGNOSTICS
|
#if JSON_DIAGNOSTICS
|
||||||
std::vector<std::string> tokens;
|
std::vector<std::string> tokens;
|
||||||
const bool found = edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens);
|
// a value that is not found leaves no tokens, like the root
|
||||||
if (!found || tokens.empty())
|
static_cast<void>(edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens));
|
||||||
|
if (!tokens.empty())
|
||||||
{
|
{
|
||||||
// context is still empty; returning it keeps the copy elided (-Wnrvo)
|
// the root value has no path, but still has a byte range
|
||||||
return context;
|
|
||||||
}
|
|
||||||
std::string path;
|
std::string path;
|
||||||
for (const std::string& token : tokens)
|
for (const std::string& token : tokens)
|
||||||
{
|
{
|
||||||
@@ -5111,6 +5109,7 @@ std::string diagnostics(const error_site site)
|
|||||||
path += detail::escape(token);
|
path += detail::escape(token);
|
||||||
}
|
}
|
||||||
context = concat('(', path, ") ");
|
context = concat('(', path, ") ");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#if JSON_DIAGNOSTIC_POSITIONS
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
std::size_t start = 0;
|
std::size_t start = 0;
|
||||||
|
|||||||
@@ -193,6 +193,28 @@ TEST_CASE("Better diagnostics with positions")
|
|||||||
check_conversion(300);
|
check_conversion(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("exception about the root value (#5987)")
|
||||||
|
{
|
||||||
|
// the root has no path, but still has a byte range
|
||||||
|
const json root = json::parse("true");
|
||||||
|
const json nested = json::parse("[1, true]");
|
||||||
|
const json constructed = true;
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK_THROWS_WITH_AS(root.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean", json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(nested[1].get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (/1) (bytes 4-8) type must be string, but is boolean", json::type_error);
|
||||||
|
#else
|
||||||
|
CHECK_THROWS_WITH_AS(root.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean", json::type_error);
|
||||||
|
CHECK_THROWS_WITH_AS(nested[1].get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] (bytes 4-8) type must be string, but is boolean", json::type_error);
|
||||||
|
#endif
|
||||||
|
// a value without positions gets no range and no stray whitespace
|
||||||
|
CHECK_THROWS_WITH_AS(constructed.get<std::string>(),
|
||||||
|
"[json.exception.type_error.302] type must be string, but is boolean", json::type_error);
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("JSON patch add to primitive parent (#4292)")
|
SECTION("JSON patch add to primitive parent (#4292)")
|
||||||
{
|
{
|
||||||
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
// the JSON Patch "add" target /foo/bar/baz has a string parent
|
||||||
@@ -243,3 +265,14 @@ TEST_CASE("values read from a binary format have no positions")
|
|||||||
check_no_positions(j);
|
check_no_positions(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("comparing a value-initialized iterator throws invalid_iterator.212")
|
||||||
|
{
|
||||||
|
// such an iterator has no value to take the byte positions from; the
|
||||||
|
// comparison used to dereference the null pointer instead of throwing
|
||||||
|
json j = {1, 2};
|
||||||
|
json::iterator it{};
|
||||||
|
CHECK_THROWS_WITH_AS(it == j.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
CHECK_THROWS_WITH_AS(it < j.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
CHECK_THROWS_WITH_AS(j.begin() == it, "[json.exception.invalid_iterator.212] cannot compare iterators of different containers", json::invalid_iterator);
|
||||||
|
}
|
||||||
@@ -300,6 +300,27 @@ TEST_CASE("json_view materialize() source positions")
|
|||||||
static_cast<void>(j.at("list").at(1).get<int>());
|
static_cast<void>(j.at("list").at(1).get<int>());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("exception about the root value (#5987)")
|
||||||
|
{
|
||||||
|
const json_document root = json_document::parse("true");
|
||||||
|
const json_document nested = json_document::parse("[1, true]");
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(root.root().get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (bytes 0-4) type must be string, but is boolean");
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(nested.root()[1].get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (/1) (bytes 4-8) type must be string, but is boolean");
|
||||||
|
#else
|
||||||
|
CHECK(json_view_test::exception_of_call([&]
|
||||||
|
{
|
||||||
|
static_cast<void>(nested.root()[1].get<std::string>());
|
||||||
|
}) == "[json.exception.type_error.302] (bytes 4-8) type must be string, but is boolean");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SECTION("edited documents")
|
SECTION("edited documents")
|
||||||
@@ -550,12 +571,8 @@ TEST_CASE("json_view exceptions name the path and the bytes of the value")
|
|||||||
expected_zz += "(bytes 12-35) key 'zz' not found";
|
expected_zz += "(bytes 12-35) key 'zz' not found";
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(d.root().at(json::json_pointer("/people/0/zz"))); }) == expected_zz);
|
CHECK(exception_of_call([&] { static_cast<void>(d.root().at(json::json_pointer("/people/0/zz"))); }) == expected_zz);
|
||||||
|
|
||||||
// the root has no path, and with JSON_DIAGNOSTICS then no bytes either (as basic_json)
|
// the root has no path, but still its bytes (#5987)
|
||||||
std::string expected_missing = "[json.exception.out_of_range.403] ";
|
const std::string expected_missing = "[json.exception.out_of_range.403] (bytes 0-79) key 'missing' not found";
|
||||||
#if !JSON_DIAGNOSTICS
|
|
||||||
expected_missing += "(bytes 0-79) ";
|
|
||||||
#endif
|
|
||||||
expected_missing += "key 'missing' not found";
|
|
||||||
CHECK(exception_of_call([&] { static_cast<void>(d.root().at("missing")); }) == expected_missing);
|
CHECK(exception_of_call([&] { static_cast<void>(d.root().at("missing")); }) == expected_missing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user