1
0
mirror of https://github.com/nlohmann/json synced 2025-01-15 03:30:04 +00:00

🚧 add diagnostics to exceptions

This commit is contained in:
Niels Lohmann 2021-01-02 13:44:41 +01:00
parent a4d491e22d
commit 7b047861b0
No known key found for this signature in database
GPG Key ID: 7F3CEA63AE251B69
5 changed files with 114 additions and 86 deletions

View File

@ -27,7 +27,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_null())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name())));
} }
n = nullptr; n = nullptr;
} }
@ -58,7 +58,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
} }
default: default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name())));
} }
} }
@ -67,7 +67,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name())));
} }
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
@ -77,7 +77,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name())));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
@ -93,7 +93,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name())));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@ -133,7 +133,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
l.clear(); l.clear();
std::transform(j.rbegin(), j.rend(), std::transform(j.rbegin(), j.rend(),
@ -150,7 +150,7 @@ void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
l.resize(j.size()); l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l), std::transform(j.begin(), j.end(), std::begin(l),
@ -241,7 +241,7 @@ void())
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " +
std::string(j.type_name()))); std::string(j.type_name())));
} }
@ -253,7 +253,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name())));
} }
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@ -265,7 +265,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name())));
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
@ -319,7 +319,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
} }
default: default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name())));
} }
} }
@ -348,14 +348,14 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name())));
} }
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
} }
@ -368,14 +368,14 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name())));
} }
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
} }

View File

@ -219,7 +219,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() +
"excessive object size: " + std::to_string(len))); "excessive object size: " + std::to_string(len)));
} }
@ -245,7 +245,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() +
"excessive array size: " + std::to_string(len))); "excessive array size: " + std::to_string(len)));
} }
@ -400,7 +400,7 @@ class json_sax_dom_callback_parser
// check object limit // check object limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len)));
} }
return true; return true;
@ -463,7 +463,7 @@ class json_sax_dom_callback_parser
// check array limit // check array limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len)));
} }
return true; return true;

View File

@ -257,7 +257,7 @@ class iter_impl
} }
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
default: default:
{ {
@ -266,7 +266,7 @@ class iter_impl
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -300,7 +300,7 @@ class iter_impl
return m_object; return m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -401,7 +401,7 @@ class iter_impl
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers"));
} }
JSON_ASSERT(m_object != nullptr); JSON_ASSERT(m_object != nullptr);
@ -438,7 +438,7 @@ class iter_impl
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers"));
} }
JSON_ASSERT(m_object != nullptr); JSON_ASSERT(m_object != nullptr);
@ -446,7 +446,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators"));
case value_t::array: case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator); return (m_it.array_iterator < other.m_it.array_iterator);
@ -494,7 +494,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators"));
case value_t::array: case value_t::array:
{ {
@ -565,7 +565,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators"));
case value_t::array: case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator; return m_it.array_iterator - other.m_it.array_iterator;
@ -586,13 +586,13 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators"));
case value_t::array: case value_t::array:
return *std::next(m_it.array_iterator, n); return *std::next(m_it.array_iterator, n);
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
default: default:
{ {
@ -601,7 +601,7 @@ class iter_impl
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -619,7 +619,7 @@ class iter_impl
return m_it.object_iterator->first; return m_it.object_iterator->first;
} }
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators"));
} }
/*! /*!

View File

@ -2155,6 +2155,9 @@ class basic_json
basic_json(basic_json&& other) noexcept basic_json(basic_json&& other) noexcept
: m_type(std::move(other.m_type)), : m_type(std::move(other.m_type)),
m_value(std::move(other.m_value)) m_value(std::move(other.m_value))
#ifdef JSON_DIAGNOSTICS
, m_parent(other.m_parent)
#endif
{ {
// check that passed value is valid // check that passed value is valid
other.assert_invariant(); other.assert_invariant();
@ -2704,12 +2707,11 @@ class basic_json
/// @} /// @}
private: std::string diagnostics() const
#ifdef JSON_DIAGNOSTICS
std::string diagnostics()
{ {
std::string result; #ifdef JSON_DIAGNOSTICS
for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) std::vector<std::string> tokens;
for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent)
{ {
switch (current->m_parent->type()) switch (current->m_parent->type())
{ {
@ -2719,7 +2721,7 @@ class basic_json
{ {
if (current->m_parent->m_value.array->operator[](i) == *current) if (current->m_parent->m_value.array->operator[](i) == *current)
{ {
result = "/" + std::to_string(i) + result; tokens.emplace_back(std::to_string(i));
continue; continue;
} }
} }
@ -2728,11 +2730,11 @@ class basic_json
case value_t::object: case value_t::object:
{ {
for (auto it : *current->m_parent->m_value.object) for (const auto& element : *current->m_parent->m_value.object)
{ {
if (it.second == *current) if (element.second == *current)
{ {
result = "/" + it.first + result; tokens.emplace_back(element.first.c_str());
continue; continue;
} }
} }
@ -2744,10 +2746,22 @@ class basic_json
} }
} }
return result; if (tokens.empty())
} {
#endif return "";
}
return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + b;
}) + ") ";
#else
return "";
#endif
}
private:
////////////////// //////////////////
// value access // // value access //
////////////////// //////////////////

View File

@ -3513,7 +3513,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_null())) if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
{ {
JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be null, but is " + std::string(j.type_name())));
} }
n = nullptr; n = nullptr;
} }
@ -3544,7 +3544,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
} }
default: default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name())));
} }
} }
@ -3553,7 +3553,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{ {
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be boolean, but is " + std::string(j.type_name())));
} }
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>(); b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
} }
@ -3563,7 +3563,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name())));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
} }
@ -3579,7 +3579,7 @@ void from_json(const BasicJsonType& j, ConstructibleStringType& s)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_string())) if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{ {
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be string, but is " + std::string(j.type_name())));
} }
s = *j.template get_ptr<const typename BasicJsonType::string_t*>(); s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
@ -3619,7 +3619,7 @@ void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
l.clear(); l.clear();
std::transform(j.rbegin(), j.rend(), std::transform(j.rbegin(), j.rend(),
@ -3636,7 +3636,7 @@ void from_json(const BasicJsonType& j, std::valarray<T>& l)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
l.resize(j.size()); l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l), std::transform(j.begin(), j.end(), std::begin(l),
@ -3727,7 +3727,7 @@ void())
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " +
std::string(j.type_name()))); std::string(j.type_name())));
} }
@ -3739,7 +3739,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{ {
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be binary, but is " + std::string(j.type_name())));
} }
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>(); bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@ -3751,7 +3751,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_object())) if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{ {
JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be object, but is " + std::string(j.type_name())));
} }
ConstructibleObjectType ret; ConstructibleObjectType ret;
@ -3805,7 +3805,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
} }
default: default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be number, but is " + std::string(j.type_name())));
} }
} }
@ -3834,14 +3834,14 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name())));
} }
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
} }
@ -3854,14 +3854,14 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
{ {
if (JSON_HEDLEY_UNLIKELY(!j.is_array())) if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(j.type_name())));
} }
m.clear(); m.clear();
for (const auto& p : j) for (const auto& p : j)
{ {
if (JSON_HEDLEY_UNLIKELY(!p.is_array())) if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
{ {
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()))); JSON_THROW(type_error::create(302, j.diagnostics() + "type must be array, but is " + std::string(p.type_name())));
} }
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>()); m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
} }
@ -5511,7 +5511,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() +
"excessive object size: " + std::to_string(len))); "excessive object size: " + std::to_string(len)));
} }
@ -5537,7 +5537,7 @@ class json_sax_dom_parser
if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() +
"excessive array size: " + std::to_string(len))); "excessive array size: " + std::to_string(len)));
} }
@ -5692,7 +5692,7 @@ class json_sax_dom_callback_parser
// check object limit // check object limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len))); JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive object size: " + std::to_string(len)));
} }
return true; return true;
@ -5755,7 +5755,7 @@ class json_sax_dom_callback_parser
// check array limit // check array limit
if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size()))
{ {
JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len))); JSON_THROW(out_of_range::create(408, ref_stack.back()->diagnostics() + "excessive array size: " + std::to_string(len)));
} }
return true; return true;
@ -11144,7 +11144,7 @@ class iter_impl
} }
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
default: default:
{ {
@ -11153,7 +11153,7 @@ class iter_impl
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -11187,7 +11187,7 @@ class iter_impl
return m_object; return m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -11288,7 +11288,7 @@ class iter_impl
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers"));
} }
JSON_ASSERT(m_object != nullptr); JSON_ASSERT(m_object != nullptr);
@ -11325,7 +11325,7 @@ class iter_impl
// if objects are not the same, the comparison is undefined // if objects are not the same, the comparison is undefined
if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object))
{ {
JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers")); JSON_THROW(invalid_iterator::create(212, m_object->diagnostics() + "cannot compare iterators of different containers"));
} }
JSON_ASSERT(m_object != nullptr); JSON_ASSERT(m_object != nullptr);
@ -11333,7 +11333,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators")); JSON_THROW(invalid_iterator::create(213, m_object->diagnostics() + "cannot compare order of object iterators"));
case value_t::array: case value_t::array:
return (m_it.array_iterator < other.m_it.array_iterator); return (m_it.array_iterator < other.m_it.array_iterator);
@ -11381,7 +11381,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators"));
case value_t::array: case value_t::array:
{ {
@ -11452,7 +11452,7 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators")); JSON_THROW(invalid_iterator::create(209, m_object->diagnostics() + "cannot use offsets with object iterators"));
case value_t::array: case value_t::array:
return m_it.array_iterator - other.m_it.array_iterator; return m_it.array_iterator - other.m_it.array_iterator;
@ -11473,13 +11473,13 @@ class iter_impl
switch (m_object->m_type) switch (m_object->m_type)
{ {
case value_t::object: case value_t::object:
JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators")); JSON_THROW(invalid_iterator::create(208, m_object->diagnostics() + "cannot use operator[] for object iterators"));
case value_t::array: case value_t::array:
return *std::next(m_it.array_iterator, n); return *std::next(m_it.array_iterator, n);
case value_t::null: case value_t::null:
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
default: default:
{ {
@ -11488,7 +11488,7 @@ class iter_impl
return *m_object; return *m_object;
} }
JSON_THROW(invalid_iterator::create(214, "cannot get value")); JSON_THROW(invalid_iterator::create(214, m_object->diagnostics() + "cannot get value"));
} }
} }
} }
@ -11506,7 +11506,7 @@ class iter_impl
return m_it.object_iterator->first; return m_it.object_iterator->first;
} }
JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators")); JSON_THROW(invalid_iterator::create(207, m_object->diagnostics() + "cannot use key() for non-object iterators"));
} }
/*! /*!
@ -18779,6 +18779,9 @@ class basic_json
basic_json(basic_json&& other) noexcept basic_json(basic_json&& other) noexcept
: m_type(std::move(other.m_type)), : m_type(std::move(other.m_type)),
m_value(std::move(other.m_value)) m_value(std::move(other.m_value))
#ifdef JSON_DIAGNOSTICS
, m_parent(other.m_parent)
#endif
{ {
// check that passed value is valid // check that passed value is valid
other.assert_invariant(); other.assert_invariant();
@ -19328,12 +19331,11 @@ class basic_json
/// @} /// @}
private: std::string diagnostics() const
#ifdef JSON_DIAGNOSTICS
std::string diagnostics()
{ {
std::string result; #ifdef JSON_DIAGNOSTICS
for (basic_json* current = this; current->m_parent != nullptr; current = current->m_parent) std::vector<std::string> tokens;
for (const basic_json* current = this; current->m_parent != nullptr; current = current->m_parent)
{ {
switch (current->m_parent->type()) switch (current->m_parent->type())
{ {
@ -19343,7 +19345,7 @@ class basic_json
{ {
if (current->m_parent->m_value.array->operator[](i) == *current) if (current->m_parent->m_value.array->operator[](i) == *current)
{ {
result = "/" + std::to_string(i) + result; tokens.emplace_back(std::to_string(i));
continue; continue;
} }
} }
@ -19352,11 +19354,11 @@ class basic_json
case value_t::object: case value_t::object:
{ {
for (auto it : *current->m_parent->m_value.object) for (const auto& element : *current->m_parent->m_value.object)
{ {
if (it.second == *current) if (element.second == *current)
{ {
result = "/" + it.first + result; tokens.emplace_back(element.first.c_str());
continue; continue;
} }
} }
@ -19368,10 +19370,22 @@ class basic_json
} }
} }
return result; if (tokens.empty())
} {
#endif return "";
}
return "(" + std::accumulate(tokens.begin(), tokens.end(), std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + b;
}) + ") ";
#else
return "";
#endif
}
private:
////////////////// //////////////////
// value access // // value access //
////////////////// //////////////////