mirror of
https://github.com/nlohmann/json.git
synced 2026-11-02 10:55:35 +00:00
json_view: exceptions name the path and the bytes of the value (#5974)
Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
1 parent
f022e3c59c
commit
ddc34f546a
25 files changed
+1600
-696
No files matched your search
@@ -431,6 +431,28 @@ std::nullptr_t exception_context(const View& /*unused*/) noexcept
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
namespace view
|
||||
{
|
||||
// the path and bytes of the value of a view (defined in <nlohmann/detail/view/errors.hpp>)
|
||||
template<typename View>
|
||||
std::string diagnostics_of(const View& v);
|
||||
} // namespace view
|
||||
|
||||
// what goes in front of the message of an exception about j: nothing for a
|
||||
// basic_json value (exception_context() passes the value itself), the context
|
||||
// basic_json would add (JSON_DIAGNOSTICS, JSON_DIAGNOSTIC_POSITIONS) for a view
|
||||
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
|
||||
const char* exception_prefix(const BasicJsonType& /*unused*/) noexcept
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
|
||||
std::string exception_prefix(const View& v)
|
||||
{
|
||||
return view::diagnostics_of(v);
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief throws because @a j does not have the type a conversion expects
|
||||
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
|
||||
@@ -442,7 +464,7 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const cha
|
||||
{
|
||||
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
||||
static_cast<void>(j);
|
||||
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), exception_context(j)));
|
||||
JSON_THROW(type_error::create(exception_id::type_mismatch, concat(exception_prefix(j), "type must be ", expected, ", but is ", j.type_name()), exception_context(j)));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -347,7 +347,7 @@
|
||||
return ej_pair.second == j; \
|
||||
}); \
|
||||
if (it != std::end(m)) e = it->first; \
|
||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::exception_context(j))); \
|
||||
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat(nlohmann::detail::exception_prefix(j), "enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::exception_context(j))); \
|
||||
}
|
||||
|
||||
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
|
||||
|
||||
@@ -157,7 +157,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != node_kind::object && o->kind != node_kind::null)
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
throw_type_error(site_of(object), exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", object.type_name());
|
||||
}
|
||||
check_utf8(key.data(), key.size());
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
@@ -208,9 +208,9 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != node_kind::array)
|
||||
{
|
||||
throw_type_error(exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
throw_type_error(site_of(array), exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
check_index(a, idx, a->len);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
node* const slot = const_cast<node*>(nav::value(element_at<true>(m_doc, a, idx))); // NOLINT(cppcoreguidelines-pro-type-const-cast)
|
||||
assign(slot, e, a, true);
|
||||
@@ -225,7 +225,7 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != node_kind::array && a->kind != node_kind::null)
|
||||
{
|
||||
throw_type_error(exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
|
||||
throw_type_error(site_of(array), exception_id::push_back_wrong_type, "cannot use push_back() with ", array.type_name());
|
||||
}
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
if (a->kind == node_kind::null)
|
||||
@@ -249,9 +249,9 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != node_kind::array)
|
||||
{
|
||||
throw_type_error(exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
|
||||
throw_type_error(site_of(array), exception_id::insert_wrong_type, "cannot use insert() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len + 1);
|
||||
check_index(a, idx, a->len + 1);
|
||||
const encoded e = encode(std::forward<V>(value));
|
||||
node* const slot = new_slot(e);
|
||||
node* const h = block_of(m_doc, a, 1);
|
||||
@@ -269,7 +269,7 @@ class editor
|
||||
node* const o = own(object);
|
||||
if (o->kind != node_kind::object)
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
|
||||
throw_type_error(site_of(object), exception_id::erase_wrong_type, "cannot use erase() with ", object.type_name());
|
||||
}
|
||||
for (const node* k = nav::first(m_doc, o), *end = nav::end(m_doc, o); k != end; k = document_data::after(k + 1))
|
||||
{
|
||||
@@ -287,9 +287,9 @@ class editor
|
||||
node* const a = own(array);
|
||||
if (a->kind != node_kind::array)
|
||||
{
|
||||
throw_type_error(exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
|
||||
throw_type_error(site_of(array), exception_id::erase_wrong_type, "cannot use erase() with ", array.type_name());
|
||||
}
|
||||
check_index(idx, a->len);
|
||||
check_index(a, idx, a->len);
|
||||
node* const h = block_of(m_doc, a, 0);
|
||||
std::memmove(h + 1 + idx, h + 2 + idx, (h->next - 2 - idx) * sizeof(node));
|
||||
--h->next;
|
||||
@@ -316,11 +316,11 @@ class editor
|
||||
return const_cast<node*>(v.m_node); // NOLINT(cppcoreguidelines-pro-type-const-cast): the nodes belong to this document
|
||||
}
|
||||
|
||||
static void check_index(std::size_t idx, std::size_t limit)
|
||||
void check_index(const node* array, std::size_t idx, std::size_t limit) const
|
||||
{
|
||||
if (idx >= limit)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_array_index_out_of_range(error_site(&m_doc, array), idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,15 @@
|
||||
|
||||
#include <algorithm> // min
|
||||
#include <cstddef> // size_t
|
||||
#include <string> // string
|
||||
#include <string> // string, to_string
|
||||
#include <vector> // vector
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <nlohmann/detail/view/builder.hpp>
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/materialize.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||
namespace detail
|
||||
@@ -22,8 +26,293 @@ namespace detail
|
||||
namespace view
|
||||
{
|
||||
|
||||
/*!
|
||||
@brief the value an exception is about
|
||||
|
||||
basic_json passes the value to the exception, which then names its path
|
||||
(JSON_DIAGNOSTICS) and its bytes in the source (JSON_DIAGNOSTIC_POSITIONS). A
|
||||
view's value has no parent pointer and no stored positions: both are found
|
||||
from the document and the node, when the exception is thrown (so only the
|
||||
error path pays for them). Without the two macros, a site is empty.
|
||||
*/
|
||||
class error_site
|
||||
{
|
||||
public:
|
||||
error_site() noexcept = default;
|
||||
|
||||
#if JSON_DIAGNOSTICS || JSON_DIAGNOSTIC_POSITIONS
|
||||
error_site(const document_data* d, const node* n) noexcept
|
||||
: m_doc(d), m_node(n)
|
||||
{}
|
||||
|
||||
const document_data* doc() const noexcept
|
||||
{
|
||||
return m_doc;
|
||||
}
|
||||
const node* value() const noexcept
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
private:
|
||||
const document_data* m_doc = nullptr;
|
||||
const node* m_node = nullptr;
|
||||
#else
|
||||
error_site(const document_data* /*d*/, const node* /*n*/) noexcept {}
|
||||
#endif
|
||||
};
|
||||
|
||||
/// the site of a view's value (the views make this a friend)
|
||||
struct view_access
|
||||
{
|
||||
template<typename View>
|
||||
static error_site site(const View& v) noexcept
|
||||
{
|
||||
return {v.m_doc, v.m_node};
|
||||
}
|
||||
};
|
||||
|
||||
template<typename View>
|
||||
error_site site_of(const View& v) noexcept
|
||||
{
|
||||
static_assert(is_json_view<View>::value, "the site of a value is only defined for views");
|
||||
return view_access::site(v);
|
||||
}
|
||||
|
||||
#if JSON_DIAGNOSTICS
|
||||
/*!
|
||||
@brief the JSON pointer tokens from the root of document @a d to node @a target
|
||||
|
||||
Nodes have no parent index, so the path is found by descending from the root:
|
||||
before the first edit, only into the subtree that contains the target (a
|
||||
container spans the next `next` nodes); after edits, elements can live in
|
||||
other sequences, so every container is searched. Iterative (the nesting depth
|
||||
is limited by memory only). False if the target is not found (then tokens is
|
||||
unspecified).
|
||||
*/
|
||||
template<bool Editable>
|
||||
bool value_path(const document_data& d, const node* target, std::vector<std::string>& tokens)
|
||||
{
|
||||
using nav = navigation<Editable>;
|
||||
struct frame
|
||||
{
|
||||
const node* pos;
|
||||
const node* end;
|
||||
std::size_t index;
|
||||
bool object;
|
||||
};
|
||||
const node* const root = d.nodes;
|
||||
if (target == root)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!is_container(*root))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
std::vector<frame> stack;
|
||||
stack.push_back(frame{nav::first(d, root), nav::end(d, root), 0, root->kind == node_kind::object});
|
||||
// (tokens[i] leads from stack[i] to stack[i + 1], or to the target)
|
||||
for (;;)
|
||||
{
|
||||
// a copy: pop_back() and push_back() below change the stack
|
||||
const frame f = stack.back();
|
||||
if (f.pos == f.end)
|
||||
{
|
||||
stack.pop_back();
|
||||
if (stack.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
tokens.pop_back();
|
||||
continue;
|
||||
}
|
||||
const node* entry = f.pos;
|
||||
std::string token;
|
||||
if (f.object)
|
||||
{
|
||||
// (as basic_json: the key up to a null byte)
|
||||
token.assign(d.str(*entry), entry->len);
|
||||
const std::size_t nul = token.find('\0');
|
||||
if (nul != std::string::npos)
|
||||
{
|
||||
token.resize(nul);
|
||||
}
|
||||
++entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
token = std::to_string(f.index);
|
||||
}
|
||||
const node* const child = nav::value(entry);
|
||||
if (child == target)
|
||||
{
|
||||
tokens.push_back(token);
|
||||
return true;
|
||||
}
|
||||
stack.back().pos = document_data::after(entry);
|
||||
++stack.back().index;
|
||||
if (is_container(*child) && (nav::editable() || (target > child && target < child + child->next)))
|
||||
{
|
||||
tokens.push_back(token);
|
||||
stack.push_back(frame{nav::first(d, child), nav::end(d, child), 0, child->kind == node_kind::object});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
/*!
|
||||
@brief the bytes [start, end) of node @a n in the source, as materialize() sets them
|
||||
|
||||
Scalars as position_source::set_scalar; an array or object from its opening
|
||||
bracket to its closing bracket, which is found by following the chain of last
|
||||
elements to a scalar (or an empty container) and scanning from there to the
|
||||
closing brackets. None (npos) for values written by edits, and for containers
|
||||
whose last element has none, as with materialize().
|
||||
*/
|
||||
template<bool Editable>
|
||||
void value_range(const document_data& d, const node* n, std::size_t& start, std::size_t& end)
|
||||
{
|
||||
using nav = navigation<Editable>;
|
||||
position_source p;
|
||||
start = std::string::npos;
|
||||
end = std::string::npos;
|
||||
if (!is_container(*n))
|
||||
{
|
||||
p.set_scalar(d, *n);
|
||||
start = p.start;
|
||||
end = p.end;
|
||||
return;
|
||||
}
|
||||
if ((n->flags & node_flags::is_new) != 0 || n->off >= d.size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the open brackets: n, then the last element as long as it is a non-empty container
|
||||
std::size_t closing = 0;
|
||||
std::size_t pos = std::string::npos;
|
||||
for (const node* c = n;;)
|
||||
{
|
||||
if (!is_container(*c))
|
||||
{
|
||||
p.set_scalar(d, *c);
|
||||
pos = p.end;
|
||||
break;
|
||||
}
|
||||
if ((c->flags & node_flags::is_new) != 0 || c->off >= d.size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
++closing;
|
||||
const node* const first = nav::first(d, c);
|
||||
const node* const last_end = nav::end(d, c);
|
||||
if (first == last_end)
|
||||
{
|
||||
pos = std::size_t{c->off} + 1;
|
||||
break;
|
||||
}
|
||||
const std::size_t value_offset = c->kind == node_kind::object ? 1 : 0;
|
||||
const node* last = first;
|
||||
for (const node* e = document_data::after(first + value_offset); e != last_end; e = document_data::after(e + value_offset))
|
||||
{
|
||||
last = e;
|
||||
}
|
||||
c = nav::value(last + value_offset);
|
||||
}
|
||||
for (; closing != 0 && pos != std::string::npos; --closing)
|
||||
{
|
||||
pos = position_source::container_end(d.base[0], d.size, pos);
|
||||
}
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
start = n->off;
|
||||
end = pos;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// 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
|
||||
/// "(bytes a-b) " with JSON_DIAGNOSTIC_POSITIONS (with JSON_DIAGNOSTICS, only
|
||||
/// if there is a path)
|
||||
///
|
||||
/// 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
|
||||
/// thrown: instantiated in every translation unit (as a non-template inline
|
||||
/// function makes it), it changed GCC's early inlining of unrelated view
|
||||
/// functions, so that GCC 13 no longer inlined a recursive walk over a view
|
||||
/// into itself (2.7 times the instructions).
|
||||
#if JSON_DIAGNOSTICS || JSON_DIAGNOSTIC_POSITIONS
|
||||
template<typename Unused = void>
|
||||
std::string diagnostics(const error_site site)
|
||||
{
|
||||
if (site.doc() == nullptr || site.value() == nullptr)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
const document_data& d = *site.doc();
|
||||
const bool edited = d.edits != nullptr;
|
||||
std::string context;
|
||||
#if JSON_DIAGNOSTICS
|
||||
std::vector<std::string> tokens;
|
||||
const bool found = edited ? value_path<true>(d, site.value(), tokens) : value_path<false>(d, site.value(), tokens);
|
||||
if (!found || tokens.empty())
|
||||
{
|
||||
// context is still empty; returning it keeps the copy elided (-Wnrvo)
|
||||
return context;
|
||||
}
|
||||
std::string path;
|
||||
for (const std::string& token : tokens)
|
||||
{
|
||||
path.push_back('/');
|
||||
path += detail::escape(token);
|
||||
}
|
||||
context = concat('(', path, ") ");
|
||||
#endif
|
||||
#if JSON_DIAGNOSTIC_POSITIONS
|
||||
std::size_t start = 0;
|
||||
std::size_t end = 0;
|
||||
if (edited)
|
||||
{
|
||||
value_range<true>(d, site.value(), start, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
value_range<false>(d, site.value(), start, end);
|
||||
}
|
||||
if (start != std::string::npos && end != std::string::npos)
|
||||
{
|
||||
context += concat("(bytes ", std::to_string(start), "-", std::to_string(end), ") ");
|
||||
}
|
||||
#endif
|
||||
return context;
|
||||
}
|
||||
#else
|
||||
template<typename Unused = void>
|
||||
std::string diagnostics(const error_site /*site*/)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
/// the context of an exception about view @a v (see exception_prefix())
|
||||
template<typename View>
|
||||
std::string diagnostics_of(const View& v)
|
||||
{
|
||||
return diagnostics<>(site_of(v));
|
||||
}
|
||||
|
||||
// Exceptions are thrown out of line, so that the accessors that may throw stay
|
||||
// small enough to be inlined.
|
||||
// small enough to be inlined. The overloads with an error_site put its context
|
||||
// in front of the message.
|
||||
//
|
||||
// The overloads without one are for exceptions that are not about a value of a
|
||||
// document: limits and invalid input of the editing and image functions, a
|
||||
// discarded view (it has no node), a view of another document (its path would
|
||||
// be one in that document), and those that basic_json creates without a
|
||||
// context too (the errors of json_pointer, invalid UTF-8). The one exception
|
||||
// about a value that has no site is key(), see view_iterator::key().
|
||||
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_type_error(exception_id id, const char* prefix, const char* type)
|
||||
{
|
||||
@@ -45,6 +334,24 @@ namespace view
|
||||
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, msg, nullptr));
|
||||
}
|
||||
|
||||
template<typename Unused = void>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_type_error(const error_site site, exception_id id, const char* prefix, const char* type)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(type_error::create(id, concat(diagnostics<>(site), prefix, type), nullptr));
|
||||
}
|
||||
|
||||
template<typename Unused = void>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_out_of_range(const error_site site, exception_id id, const std::string& msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(out_of_range::create(id, concat(diagnostics<>(site), msg), nullptr));
|
||||
}
|
||||
|
||||
template<typename Unused = void>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_invalid_iterator(const error_site site, exception_id id, const char* msg)
|
||||
{
|
||||
NLOHMANN_VIEW_THROW(invalid_iterator::create(id, concat(diagnostics<>(site), msg), nullptr));
|
||||
}
|
||||
|
||||
/// a parse error without a position (as those of json_pointer)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_parse_error(exception_id id, const std::string& msg)
|
||||
{
|
||||
@@ -53,22 +360,23 @@ namespace view
|
||||
|
||||
/// out_of_range.401: the array index @a idx is not below the size
|
||||
template<typename Index>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const Index idx)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_out_of_range(const error_site site, const Index idx)
|
||||
{
|
||||
throw_out_of_range(exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
throw_out_of_range(site, exception_id::array_index_out_of_range, concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.402: the JSON Pointer token '-' of an array of @a size elements
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE inline void throw_array_index_past_end(std::size_t size)
|
||||
template<typename Unused = void>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_past_end(const error_site site, std::size_t size)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range"));
|
||||
throw_out_of_range(site, exception_id::pointer_past_the_end_index, concat("array index '-' (", std::to_string(size), ") is out of range"));
|
||||
}
|
||||
|
||||
/// out_of_range.403: the object has no member @a key
|
||||
template<typename Key>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const Key& key)
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_key_not_found(const error_site site, const Key& key)
|
||||
{
|
||||
throw_out_of_range(exception_id::key_not_found, concat("key '", key, "' not found"));
|
||||
throw_out_of_range(site, exception_id::key_not_found, concat("key '", key, "' not found"));
|
||||
}
|
||||
|
||||
/// SAX handler of the error replay: accepts every event without storing it,
|
||||
|
||||
@@ -105,7 +105,10 @@ class view_iterator
|
||||
}
|
||||
|
||||
/// the key of the current object member; throws invalid_iterator.207 for
|
||||
/// other iterators, like basic_json's iterators
|
||||
/// other iterators, like basic_json's iterators (but without the context
|
||||
/// JSON_DIAGNOSTICS and JSON_DIAGNOSTIC_POSITIONS add: the iterator does not
|
||||
/// know the value it iterates over, and keeping it would make iterators
|
||||
/// larger, which keeps GCC from inlining recursive walks)
|
||||
string_view_t key() const
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
||||
|
||||
@@ -44,6 +44,7 @@ index_status array_index(const StringType& s, std::size_t& idx) noexcept
|
||||
}
|
||||
|
||||
/// throws the exception json_pointer::array_index throws for this status
|
||||
/// (without a context, as json_pointer::array_index)
|
||||
template<typename StringType>
|
||||
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_error(index_status status, const StringType& s)
|
||||
{
|
||||
@@ -83,7 +84,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_key_not_found(token);
|
||||
throw_key_not_found(site_of(cur), token);
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -95,7 +96,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_array_index_past_end(cur.size());
|
||||
throw_array_index_past_end(site_of(cur), cur.size());
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -114,7 +115,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (mode == pointer_mode::checked)
|
||||
{
|
||||
throw_array_index_out_of_range(idx);
|
||||
throw_array_index_out_of_range(site_of(cur), idx);
|
||||
}
|
||||
return View();
|
||||
}
|
||||
@@ -124,7 +125,7 @@ View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
||||
{
|
||||
if (throwing)
|
||||
{
|
||||
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'"));
|
||||
throw_out_of_range(site_of(cur), exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'"));
|
||||
}
|
||||
return View();
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ NLOHMANN_VIEW_ALWAYS_INLINE T arithmetic_value(const document_data& d, const nod
|
||||
case value_t::binary:
|
||||
case value_t::discarded:
|
||||
default:
|
||||
throw_type_error(exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
throw_type_error(error_site(&d, &n), exception_id::type_mismatch, "type must be number, but is ", value_type_name(static_cast<value_t>(n.kind)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ std::vector<U, A> vector_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_array()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be array, but is ", v.type_name());
|
||||
throw_type_error(site_of(v), exception_id::type_mismatch, "type must be array, but is ", v.type_name());
|
||||
}
|
||||
std::vector<U, A> r;
|
||||
r.reserve(v.size());
|
||||
@@ -88,7 +88,7 @@ Map map_value(const View& v)
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!v.is_object()))
|
||||
{
|
||||
throw_type_error(exception_id::type_mismatch, "type must be object, but is ", v.type_name());
|
||||
throw_type_error(site_of(v), exception_id::type_mismatch, "type must be object, but is ", v.type_name());
|
||||
}
|
||||
Map r;
|
||||
for (auto it = v.begin(); it != v.end(); ++it)
|
||||
|
||||
@@ -285,7 +285,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::subscript_wrong_type, "cannot use operator[] with a string argument with ", type_name());
|
||||
}
|
||||
return lookup(key);
|
||||
}
|
||||
@@ -311,7 +311,7 @@ class basic_json_view
|
||||
{
|
||||
return {};
|
||||
}
|
||||
detail::view::throw_type_error(detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::subscript_wrong_type, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
}
|
||||
return idx < m_node->len ? element(idx) : basic_json_view();
|
||||
}
|
||||
@@ -344,12 +344,12 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(r.is_discarded()))
|
||||
{
|
||||
detail::view::throw_key_not_found(std::string(key.data(), key.size()));
|
||||
detail::view::throw_key_not_found(site(), std::string(key.data(), key.size()));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -370,11 +370,11 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::at_wrong_type, "cannot use at() with ", type_name());
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len))
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_array_index_out_of_range(site(), idx);
|
||||
}
|
||||
return element(idx);
|
||||
}
|
||||
@@ -402,7 +402,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -412,7 +412,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
}
|
||||
// (the default is made only if there is no such member)
|
||||
const basic_json_view r = lookup(key);
|
||||
@@ -427,7 +427,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_structured()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value);
|
||||
return r.is_discarded() ? default_value : r.template get<T>();
|
||||
@@ -437,7 +437,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_structured()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::value_wrong_type, "cannot use value() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = detail::view::resolve_pointer(*this, detail::json_pointer_access::reference_tokens(ptr), detail::view::pointer_mode::value);
|
||||
return r.is_discarded() ? string_t(default_value) : r.template get<string_t>();
|
||||
@@ -450,7 +450,7 @@ class basic_json_view
|
||||
const iterator it = begin();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(it == end()))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(detail::exception_id::iterator_value_unavailable, "cannot get value");
|
||||
detail::view::throw_invalid_iterator(site(), detail::exception_id::iterator_value_unavailable, "cannot get value");
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
@@ -606,7 +606,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_string()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be string, but is ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::type_mismatch, "type must be string, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), m_node->len};
|
||||
}
|
||||
@@ -618,7 +618,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_number()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
}
|
||||
return {m_doc->str(*m_node), detail::view::number_length(*m_node)};
|
||||
}
|
||||
@@ -764,12 +764,19 @@ class basic_json_view
|
||||
template<typename, view_mode> friend class basic_json_view;
|
||||
template<typename, typename> friend class detail::view::editor;
|
||||
template<typename, typename> friend class detail::view::view_side;
|
||||
friend struct detail::view::view_access;
|
||||
friend iterator;
|
||||
|
||||
basic_json_view(const document_data* d, const node* n) noexcept
|
||||
: m_doc(d), m_node(n)
|
||||
{}
|
||||
|
||||
/// this value as the context of an exception
|
||||
detail::view::error_site site() const noexcept
|
||||
{
|
||||
return {m_doc, m_node};
|
||||
}
|
||||
|
||||
using json_side_t = detail::view::json_side<BasicJsonType, string_view_t>;
|
||||
|
||||
static detail::view::view_side<BasicJsonType, basic_json_view> side(const basic_json_view& v) noexcept
|
||||
@@ -876,7 +883,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_boolean()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::type_mismatch, "type must be boolean, but is ", type_name());
|
||||
}
|
||||
return (m_node->flags & detail::view::node_flags::is_true) != 0;
|
||||
}
|
||||
@@ -886,7 +893,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(m_node == nullptr))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::type_mismatch, "type must be number, but is ", type_name());
|
||||
}
|
||||
return detail::view::arithmetic_value<T, BasicJsonType>(*m_doc, *m_node);
|
||||
}
|
||||
@@ -895,7 +902,7 @@ class basic_json_view
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_null()))
|
||||
{
|
||||
detail::view::throw_type_error(detail::exception_id::type_mismatch, "type must be null, but is ", type_name());
|
||||
detail::view::throw_type_error(site(), detail::exception_id::type_mismatch, "type must be null, but is ", type_name());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1310,7 +1317,7 @@ class basic_json_document
|
||||
template<typename I, typename V, typename std::enable_if<detail::view::is_non_bool_integral<I>::value, int>::type = 0>
|
||||
view_type set(view_type array, I idx, V && value)
|
||||
{
|
||||
return editor().set(array, index(idx), std::forward<V>(value));
|
||||
return editor().set(array, index(array, idx), std::forward<V>(value));
|
||||
}
|
||||
|
||||
/// set the value at a JSON pointer: its parent must exist; an object
|
||||
@@ -1347,7 +1354,7 @@ class basic_json_document
|
||||
const std::size_t idx = pointer_index(token);
|
||||
if (idx >= 0xFFFFFFFFu)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_array_index_out_of_range(detail::view::site_of(parent), idx);
|
||||
}
|
||||
for (std::size_t i = 0; i < idx; ++i)
|
||||
{
|
||||
@@ -1381,7 +1388,7 @@ class basic_json_document
|
||||
template<typename I, typename V, typename std::enable_if<detail::view::is_non_bool_integral<I>::value, int>::type = 0>
|
||||
view_type insert(view_type array, I idx, V && value)
|
||||
{
|
||||
return editor().insert(array, index(idx), std::forward<V>(value));
|
||||
return editor().insert(array, index(array, idx), std::forward<V>(value));
|
||||
}
|
||||
|
||||
/// remove all members with this key; returns their number
|
||||
@@ -1394,7 +1401,7 @@ class basic_json_document
|
||||
template<typename I, typename std::enable_if<detail::view::is_non_bool_integral<I>::value, int>::type = 0>
|
||||
void erase(view_type array, I idx)
|
||||
{
|
||||
editor().erase(array, index(idx));
|
||||
editor().erase(array, index(array, idx));
|
||||
}
|
||||
|
||||
/// remove the value at a JSON pointer; returns the number of removed
|
||||
@@ -1428,25 +1435,25 @@ class basic_json_document
|
||||
return detail::view::editor<BasicJsonType, view_type>(*m_data);
|
||||
}
|
||||
|
||||
/// an index (out_of_range.401 if negative)
|
||||
/// an index into @a array (out_of_range.401 if negative)
|
||||
template<typename I>
|
||||
static std::size_t index(I idx)
|
||||
static std::size_t index(const view_type& array, I idx)
|
||||
{
|
||||
return index(idx, std::is_signed<I> {});
|
||||
return index(array, idx, std::is_signed<I> {});
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
static std::size_t index(I idx, std::true_type /*signed*/)
|
||||
static std::size_t index(const view_type& array, I idx, std::true_type /*signed*/)
|
||||
{
|
||||
if (idx < 0)
|
||||
{
|
||||
detail::view::throw_array_index_out_of_range(idx);
|
||||
detail::view::throw_array_index_out_of_range(detail::view::site_of(array), idx);
|
||||
}
|
||||
return static_cast<std::size_t>(idx);
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
static std::size_t index(I idx, std::false_type /*unsigned*/)
|
||||
static std::size_t index(const view_type& /*array*/, I idx, std::false_type /*unsigned*/)
|
||||
{
|
||||
return static_cast<std::size_t>(idx);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user