1
0
mirror of https://github.com/nlohmann/json synced 2025-01-13 02:40:06 +00:00

Merge pull request #2562 from nlohmann/diagnostics

Better diagnostics
This commit is contained in:
Niels Lohmann 2021-02-10 07:20:54 +01:00 committed by GitHub
commit 176d8e261a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1473 additions and 696 deletions

View File

@ -34,6 +34,7 @@ option(JSON_BuildTests "Build the unit tests when BUILD_TESTING is enabled." ${M
option(JSON_Install "Install CMake targets during install step." ${MAIN_PROJECT})
option(JSON_MultipleHeaders "Use non-amalgamated version of the library." OFF)
option(JSON_ImplicitConversions "Enable implicit conversions." ON)
option(JSON_Diagnostics "Enable better diagnostic messages." OFF)
##
## CONFIGURATION
@ -63,6 +64,10 @@ if (NOT JSON_ImplicitConversions)
message(STATUS "Implicit conversions are disabled")
endif()
if (JSON_Diagnostics)
message(STATUS "Diagnostics enabled")
endif()
##
## TARGET
## create target and add include path
@ -79,6 +84,7 @@ target_compile_definitions(
${NLOHMANN_JSON_TARGET_NAME}
INTERFACE
JSON_USE_IMPLICIT_CONVERSIONS=$<BOOL:${JSON_ImplicitConversions}>
JSON_DIAGNOSTICS=$<BOOL:${JSON_Diagnostics}>
)
target_include_directories(

View File

@ -0,0 +1,22 @@
#include <iostream>
# define JSON_DIAGNOSTICS 1
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
json j;
j["address"]["street"] = "Fake Street";
j["address"]["housenumber"] = "12";
try
{
int housenumber = j["address"]["housenumber"];
}
catch (json::exception& e)
{
std::cout << e.what() << '\n';
}
}

View File

@ -0,0 +1 @@
<a target="_blank" href="https://wandbox.org/permlink/pbz3ULoJ4maRnV8N"><b>online</b></a>

View File

@ -0,0 +1 @@
[json.exception.type_error.302] (/address/housenumber) type must be number, but is string

View File

@ -0,0 +1,20 @@
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main()
{
json j;
j["address"]["street"] = "Fake Street";
j["address"]["housenumber"] = "12";
try
{
int housenumber = j["address"]["housenumber"];
}
catch (json::exception& e)
{
std::cout << e.what() << '\n';
}
}

View File

@ -0,0 +1 @@
<a target="_blank" href="https://wandbox.org/permlink/fWfQhHzG03P6PAcC"><b>online</b></a>

View File

@ -0,0 +1 @@
[json.exception.type_error.302] type must be number, but is string

View File

@ -12,6 +12,14 @@ This macro overrides `#!cpp catch` calls inside the library. The argument is the
See [Switch off exceptions](../home/exceptions.md#switch-off-exceptions) for an example.
## `JSON_DIAGNOSTICS`
This macro enables extended diagnostics for exception messages. Possible values are `1` to enable or `0` to disable (default).
When enabled, exception messages contain a [JSON Pointer](json_pointer.md) to the JSON value that triggered the exception, see [Extended diagnostic messages](../home/exceptions.md#extended-diagnostic-messages) for an example. Note that enabling this macro increases the size of every JSON value by one pointer and adds some runtime overhead.
The diagnostics messages can also be controlled with the CMake option `JSON_Diagnostics` (`OFF` by default) which sets `JSON_DIAGNOSTICS` accordingly.
## `JSON_NOEXCEPTION`
Exceptions can be switched off by defining the symbol `JSON_NOEXCEPTION`.
@ -56,6 +64,8 @@ When defined to `0`, implicit conversions are switched off. By default, implicit
auto s = j.get<std::string>();
```
Implicit conversions can also be controlled with the CMake option `JSON_ImplicitConversions` (`ON` by default) which sets `JSON_USE_IMPLICIT_CONVERSIONS` accordingly.
## `NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...)`
This macro can be used to simplify the serialization/deserialization of types if (1) want to use a JSON object as serialization and (2) want to use the member variable names as object keys in that object.

View File

@ -50,6 +50,43 @@ Note that `JSON_THROW_USER` should leave the current scope (e.g., by throwing or
#include <nlohmann/json.hpp>
```
### Extended diagnostic messages
Exceptions in the library are thrown in the local context of the JSON value they are detected. This makes detailed diagnostics messages, and hence debugging, difficult.
??? example
```cpp
--8<-- "examples/diagnostics_standard.cpp"
```
Output:
```
--8<-- "examples/diagnostics_standard.output"
```
This exception can be hard to debug if storing the value `#!c "12"` and accessing it is further apart.
To create better diagnostics messages, each JSON value needs a pointer to its parent value such that a global context (i.e., a path from the root value to the value that lead to the exception) can be created. That global context is provided as [JSON Pointer](../features/json_pointer.md).
As this global context comes at the price of storing one additional pointer per JSON value and runtime overhead to maintain the parent relation, extended diagnostics are disabled by default. They can, however, be enabled by defining the preprocessor symbol [`JSON_DIAGNOSTICS`](../features/macros.md#json_diagnostics) to `1` before including `json.hpp`.
??? example
```cpp
--8<-- "examples/diagnostics_extended.cpp"
```
Output:
```
--8<-- "examples/diagnostics_extended.output"
```
Now the exception message contains a JSON Pointer `/address/housenumber` that indicates which value has the wrong type.
## Parse errors
This exception is thrown by the library when a parse error occurs. Parse errors

View File

@ -4,7 +4,7 @@
The class is licensed under the [MIT License](https://opensource.org/licenses/MIT):
Copyright &copy; 2013-2020 [Niels Lohmann](https://nlohmann.me)
Copyright &copy; 2013-2021 [Niels Lohmann](https://nlohmann.me)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -27,7 +27,7 @@ void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
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, "type must be null, but is " + std::string(j.type_name()), j));
}
n = nullptr;
}
@ -58,7 +58,7 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
}
}
@ -67,7 +67,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
{
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, "type must be boolean, but is " + std::string(j.type_name()), j));
}
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()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
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()))
{
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j));
}
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()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.clear();
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()))
{
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j));
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
@ -241,8 +241,7 @@ void())
{
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, "type must be array, but is " + std::string(j.type_name()), j));
}
from_json_array_impl(j, arr, priority_tag<3> {});
@ -253,7 +252,7 @@ void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
{
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, "type must be binary, but is " + std::string(j.type_name()), j));
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
@ -265,7 +264,7 @@ void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
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, "type must be object, but is " + std::string(j.type_name()), j));
}
ConstructibleObjectType ret;
@ -319,7 +318,7 @@ void from_json(const BasicJsonType& j, ArithmeticType& val)
}
default:
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j));
}
}
@ -348,14 +347,14 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
{
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, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
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, "type must be array, but is " + std::string(p.type_name()), j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}
@ -368,14 +367,14 @@ void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyE
{
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, "type must be array, but is " + std::string(j.type_name()), j));
}
m.clear();
for (const auto& p : j)
{
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, "type must be array, but is " + std::string(p.type_name()), j));
}
m.emplace(p.at(0).template get<Key>(), p.at(1).template get<Value>());
}

View File

@ -132,6 +132,7 @@ struct external_constructor<value_t::array>
{
j.m_type = value_t::array;
j.m_value = arr;
j.set_parents();
j.assert_invariant();
}
@ -140,6 +141,7 @@ struct external_constructor<value_t::array>
{
j.m_type = value_t::array;
j.m_value = std::move(arr);
j.set_parents();
j.assert_invariant();
}
@ -152,6 +154,7 @@ struct external_constructor<value_t::array>
using std::end;
j.m_type = value_t::array;
j.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
j.set_parents();
j.assert_invariant();
}
@ -164,6 +167,7 @@ struct external_constructor<value_t::array>
for (const bool x : arr)
{
j.m_value.array->push_back(x);
j.set_parent(j.m_value.array->back());
}
j.assert_invariant();
}
@ -179,6 +183,7 @@ struct external_constructor<value_t::array>
{
std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin());
}
j.set_parents();
j.assert_invariant();
}
};
@ -191,6 +196,7 @@ struct external_constructor<value_t::object>
{
j.m_type = value_t::object;
j.m_value = obj;
j.set_parents();
j.assert_invariant();
}
@ -199,6 +205,7 @@ struct external_constructor<value_t::object>
{
j.m_type = value_t::object;
j.m_value = std::move(obj);
j.set_parents();
j.assert_invariant();
}
@ -211,6 +218,7 @@ struct external_constructor<value_t::object>
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
j.set_parents();
j.assert_invariant();
}
};

View File

@ -4,6 +4,8 @@
#include <stdexcept> // runtime_error
#include <string> // to_string
#include <nlohmann/detail/value_t.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/input/position_t.hpp>
#include <nlohmann/detail/macro_scope.hpp>
@ -65,6 +67,61 @@ class exception : public std::exception
return "[json.exception." + ename + "." + std::to_string(id_) + "] ";
}
template<typename BasicJsonType>
static std::string diagnostics(const BasicJsonType& leaf_element)
{
#if JSON_DIAGNOSTICS
std::vector<std::string> tokens;
for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent)
{
switch (current->m_parent->type())
{
case value_t::array:
{
for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i)
{
if (&current->m_parent->m_value.array->operator[](i) == current)
{
tokens.emplace_back(std::to_string(i));
break;
}
}
break;
}
case value_t::object:
{
for (const auto& element : *current->m_parent->m_value.object)
{
if (&element.second == current)
{
tokens.emplace_back(element.first.c_str());
break;
}
}
break;
}
default: // LCOV_EXCL_LINE
break; // LCOV_EXCL_LINE
}
}
if (tokens.empty())
{
return "";
}
return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + detail::escape(b);
}) + ") ";
#else
return "";
#endif
}
private:
/// an exception object as storage for error messages
std::runtime_error m;
@ -127,18 +184,20 @@ class parse_error : public exception
@param[in] what_arg the explanatory string
@return parse_error object
*/
static parse_error create(int id_, const position_t& pos, const std::string& what_arg)
template<typename BasicJsonType>
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
position_string(pos) + ": " + what_arg;
position_string(pos) + ": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, pos.chars_read_total, w.c_str());
}
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg)
template<typename BasicJsonType>
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("parse_error", id_) + "parse error" +
(byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") +
": " + what_arg;
": " + exception::diagnostics(context) + what_arg;
return parse_error(id_, byte_, w.c_str());
}
@ -204,9 +263,10 @@ caught.,invalid_iterator}
class invalid_iterator : public exception
{
public:
static invalid_iterator create(int id_, const std::string& what_arg)
template<typename BasicJsonType>
static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("invalid_iterator", id_) + what_arg;
std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg;
return invalid_iterator(id_, w.c_str());
}
@ -258,9 +318,10 @@ caught.,type_error}
class type_error : public exception
{
public:
static type_error create(int id_, const std::string& what_arg)
template<typename BasicJsonType>
static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("type_error", id_) + what_arg;
std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg;
return type_error(id_, w.c_str());
}
@ -305,9 +366,10 @@ caught.,out_of_range}
class out_of_range : public exception
{
public:
static out_of_range create(int id_, const std::string& what_arg)
template<typename BasicJsonType>
static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("out_of_range", id_) + what_arg;
std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg;
return out_of_range(id_, w.c_str());
}
@ -343,9 +405,10 @@ caught.,other_error}
class other_error : public exception
{
public:
static other_error create(int id_, const std::string& what_arg)
template<typename BasicJsonType>
static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context)
{
std::string w = exception::name("other_error", id_) + what_arg;
std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg;
return other_error(id_, w.c_str());
}

View File

@ -137,7 +137,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current != std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, get_token_string(),
parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value")));
parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType()));
}
}
@ -213,7 +213,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 1))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType()));
}
return get_string(input_format_t::bson, len - static_cast<NumberType>(1), result) && get() != std::char_traits<char_type>::eof();
@ -234,7 +234,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(len < 0))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType()));
}
// All BSON binary values have a subtype
@ -316,7 +316,7 @@ class binary_reader
{
std::array<char, 3> cr{{}};
(std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast<unsigned char>(element_type));
return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data())));
return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType()));
}
}
}
@ -716,7 +716,7 @@ class binary_reader
case cbor_tag_handler_t::error:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
case cbor_tag_handler_t::ignore:
@ -831,7 +831,7 @@ class binary_reader
default: // anything else (0xFF is handled inside the other types)
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@ -926,7 +926,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
}
@ -1025,7 +1025,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType()));
}
}
}
@ -1492,7 +1492,7 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@ -1574,7 +1574,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
}
@ -1824,7 +1824,7 @@ class binary_reader
default:
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType()));
}
}
@ -1894,7 +1894,7 @@ class binary_reader
default:
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType()));
}
}
}
@ -1932,7 +1932,7 @@ class binary_reader
return false;
}
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType()));
}
return get_ubjson_size_value(result.first);
@ -2022,7 +2022,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current > 127))
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char")));
return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType()));
}
string_t s(1, static_cast<typename string_t::value_type>(current));
return sax->string(s);
@ -2043,7 +2043,7 @@ class binary_reader
default: // anything else
{
auto last_token = get_token_string();
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value")));
return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType()));
}
}
}
@ -2221,7 +2221,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input))
{
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number")));
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
}
switch (result_number)
@ -2233,7 +2233,7 @@ class binary_reader
case token_type::value_float:
return sax->number_float(number_lexer.get_number_float(), std::move(number_string));
default:
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number")));
return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType()));
}
}
@ -2389,7 +2389,7 @@ class binary_reader
if (JSON_HEDLEY_UNLIKELY(current == std::char_traits<char_type>::eof()))
{
return sax->parse_error(chars_read, "<end of file>",
parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context)));
parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType()));
}
return true;
}

View File

@ -219,8 +219,7 @@ class json_sax_dom_parser
if (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, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@ -235,6 +234,7 @@ class json_sax_dom_parser
bool end_object()
{
ref_stack.back()->set_parents();
ref_stack.pop_back();
return true;
}
@ -245,8 +245,7 @@ class json_sax_dom_parser
if (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, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@ -254,6 +253,7 @@ class json_sax_dom_parser
bool end_array()
{
ref_stack.back()->set_parents();
ref_stack.pop_back();
return true;
}
@ -400,7 +400,7 @@ class json_sax_dom_callback_parser
// check object limit
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, "excessive object size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@ -425,10 +425,17 @@ class json_sax_dom_callback_parser
bool end_object()
{
if (ref_stack.back() && !callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
if (ref_stack.back())
{
// discard object
*ref_stack.back() = discarded;
if (!callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back()))
{
// discard object
*ref_stack.back() = discarded;
}
else
{
ref_stack.back()->set_parents();
}
}
JSON_ASSERT(!ref_stack.empty());
@ -463,7 +470,7 @@ class json_sax_dom_callback_parser
// check array limit
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, "excessive array size: " + std::to_string(len), *ref_stack.back()));
}
return true;
@ -476,7 +483,11 @@ class json_sax_dom_callback_parser
if (ref_stack.back())
{
keep = callback(static_cast<int>(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back());
if (!keep)
if (keep)
{
ref_stack.back()->set_parents();
}
else
{
// discard array
*ref_stack.back() = discarded;
@ -574,7 +585,7 @@ class json_sax_dom_callback_parser
// array
if (ref_stack.back()->is_array())
{
ref_stack.back()->m_value.array->push_back(std::move(value));
ref_stack.back()->m_value.array->emplace_back(std::move(value));
return {true, &(ref_stack.back()->m_value.array->back())};
}

View File

@ -88,7 +88,6 @@ class parser
{
json_sax_dom_callback_parser<BasicJsonType> sdp(result, callback, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
@ -96,7 +95,7 @@ class parser
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
@ -117,15 +116,13 @@ class parser
{
json_sax_dom_parser<BasicJsonType> sdp(result, allow_exceptions);
sax_parse_internal(&sdp);
result.assert_invariant();
// in strict mode, input must be completely read
if (strict && (get_token() != token_type::end_of_input))
{
sdp.parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
// in case of an error, return discarded value
@ -135,6 +132,8 @@ class parser
return;
}
}
result.assert_invariant();
}
/*!
@ -161,8 +160,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_of_input, "value")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType()));
}
return result;
@ -208,8 +206,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::value_string, "object key")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
{
@ -221,8 +218,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::name_separator, "object separator")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
}
// remember we are now inside an object
@ -265,7 +261,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'"));
out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string())))
@ -335,16 +331,14 @@ class parser
// using "uninitialized" to avoid "expected" message
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::uninitialized, "value")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType()));
}
default: // the last token was unexpected
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::literal_or_value, "value")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType()));
}
}
}
@ -390,8 +384,7 @@ class parser
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_array, "array")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType()));
}
// states.back() is false -> object
@ -404,8 +397,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::value_string, "object key")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType()));
}
if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string())))
@ -418,8 +410,7 @@ class parser
{
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::name_separator, "object separator")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType()));
}
// parse values
@ -447,8 +438,7 @@ class parser
return sax->parse_error(m_lexer.get_position(),
m_lexer.get_token_string(),
parse_error::create(101, m_lexer.get_position(),
exception_message(token_type::end_object, "object")));
parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType()));
}
}

View File

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

View File

@ -10,6 +10,7 @@
#include <nlohmann/detail/exceptions.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/string_escape.hpp>
#include <nlohmann/detail/value_t.hpp>
namespace nlohmann
@ -67,7 +68,7 @@ class json_pointer
std::string{},
[](const std::string & a, const std::string & b)
{
return a + "/" + escape(b);
return a + "/" + detail::escape(b);
});
}
@ -247,7 +248,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
reference_tokens.pop_back();
@ -271,7 +272,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
return reference_tokens.back();
@ -337,15 +338,13 @@ class json_pointer
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0'))
{
JSON_THROW(detail::parse_error::create(106, 0,
"array index '" + s +
"' must not begin with '0'"));
JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType()));
}
// error condition (cf. RFC 6901, Sect. 4)
if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9')))
{
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number"));
JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType()));
}
std::size_t processed_chars = 0;
@ -356,20 +355,20 @@ class json_pointer
}
JSON_CATCH(std::out_of_range&)
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// check if the string was completely read
if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size()))
{
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType()));
}
// only triggered on special platforms (like 32bit), see also
// https://github.com/nlohmann/json/pull/2203
if (res >= static_cast<unsigned long long>((std::numeric_limits<size_type>::max)()))
{
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type")); // LCOV_EXCL_LINE
JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE
}
return static_cast<size_type>(res);
@ -380,7 +379,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(empty()))
{
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent"));
JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType()));
}
json_pointer result = *this;
@ -443,7 +442,7 @@ class json_pointer
single value; that is, with an empty list of reference tokens.
*/
default:
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten"));
JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j));
}
}
@ -515,7 +514,7 @@ class json_pointer
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@ -548,7 +547,7 @@ class json_pointer
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
") is out of range", *ptr));
}
// note: at performs range check
@ -557,7 +556,7 @@ class json_pointer
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@ -595,9 +594,7 @@ class json_pointer
if (JSON_HEDLEY_UNLIKELY(reference_token == "-"))
{
// "-" cannot be used for const access
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr));
}
// use unchecked array access
@ -606,7 +603,7 @@ class json_pointer
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@ -639,7 +636,7 @@ class json_pointer
// "-" always fails the range check
JSON_THROW(detail::out_of_range::create(402,
"array index '-' (" + std::to_string(ptr->m_value.array->size()) +
") is out of range"));
") is out of range", *ptr));
}
// note: at performs range check
@ -648,7 +645,7 @@ class json_pointer
}
default:
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'"));
JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr));
}
}
@ -752,9 +749,7 @@ class json_pointer
// check if nonempty reference string begins with slash
if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/'))
{
JSON_THROW(detail::parse_error::create(107, 1,
"JSON pointer must be empty or begin with '/' - was: '" +
reference_string + "'"));
JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType()));
}
// extract the reference tokens:
@ -789,58 +784,18 @@ class json_pointer
(reference_token[pos + 1] != '0' &&
reference_token[pos + 1] != '1')))
{
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'"));
JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType()));
}
}
// finally, store the reference token
unescape(reference_token);
detail::unescape(reference_token);
result.push_back(reference_token);
}
return result;
}
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
static void replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
JSON_ASSERT(!f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
JSON_PRIVATE_UNLESS_TESTED:
/// escape "~" to "~0" and "/" to "~1"
static std::string escape(std::string s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/// unescape "~1" to tilde and "~0" to slash (order is important!)
static void unescape(std::string& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
}
private:
/*!
@param[in] reference_string the reference string to the current value
@ -886,7 +841,7 @@ class json_pointer
// iterate object and use keys as reference string
for (const auto& element : *value.m_value.object)
{
flatten(reference_string + "/" + escape(element.first), element.second, result);
flatten(reference_string + "/" + detail::escape(element.first), element.second, result);
}
}
break;
@ -916,7 +871,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(!value.is_object()))
{
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened"));
JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value));
}
BasicJsonType result;
@ -926,7 +881,7 @@ class json_pointer
{
if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive()))
{
JSON_THROW(detail::type_error::create(315, "values in object must be primitive"));
JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second));
}
// assign value to reference pointed to by JSON pointer; Note that if

View File

@ -57,7 +57,7 @@ class binary_writer
default:
{
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name())));
JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j));;
}
}
}
@ -901,13 +901,12 @@ class binary_writer
@return The size of a BSON document entry header, including the id marker
and the entry name size (and its null-terminator).
*/
static std::size_t calc_bson_entry_header_size(const string_t& name)
static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j)
{
const auto it = name.find(static_cast<typename string_t::value_type>(0));
if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos))
{
JSON_THROW(out_of_range::create(409,
"BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")"));
JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j));
}
return /*id*/ 1ul + name.size() + /*zero-terminator*/1u;
@ -1017,21 +1016,21 @@ class binary_writer
@brief Writes a BSON element with key @a name and unsigned @a value
*/
void write_bson_unsigned(const string_t& name,
const std::uint64_t value)
const BasicJsonType& j)
{
if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int32_t>::max)()))
{
write_bson_entry_header(name, 0x10 /* int32 */);
write_number<std::int32_t, true>(static_cast<std::int32_t>(value));
write_number<std::int32_t, true>(static_cast<std::int32_t>(j.m_value.number_unsigned));
}
else if (value <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
else if (j.m_value.number_unsigned <= static_cast<std::uint64_t>((std::numeric_limits<std::int64_t>::max)()))
{
write_bson_entry_header(name, 0x12 /* int64 */);
write_number<std::int64_t, true>(static_cast<std::int64_t>(value));
write_number<std::int64_t, true>(static_cast<std::int64_t>(j.m_value.number_unsigned));
}
else
{
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(value) + " cannot be represented by BSON as it does not fit int64"));
JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j));
}
}
@ -1108,7 +1107,7 @@ class binary_writer
static std::size_t calc_bson_element_size(const string_t& name,
const BasicJsonType& j)
{
const auto header_size = calc_bson_entry_header_size(name);
const auto header_size = calc_bson_entry_header_size(name, j);
switch (j.type())
{
case value_t::object:
@ -1177,7 +1176,7 @@ class binary_writer
return write_bson_integer(name, j.m_value.number_integer);
case value_t::number_unsigned:
return write_bson_unsigned(name, j.m_value.number_unsigned);
return write_bson_unsigned(name, j);
case value_t::string:
return write_bson_string(name, *j.m_value.string);

View File

@ -499,7 +499,7 @@ class serializer
{
std::string sn(3, '\0');
(std::snprintf)(&sn[0], sn.size(), "%.2X", byte);
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn));
JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:
@ -593,7 +593,7 @@ class serializer
{
std::string sn(3, '\0');
(std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast<std::uint8_t>(s.back()));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn));
JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType()));
}
case error_handler_t::ignore:

View File

@ -0,0 +1,63 @@
#pragma once
#include <string>
#include <nlohmann/detail/macro_scope.hpp>
namespace nlohmann
{
namespace detail
{
/*!
@brief replace all occurrences of a substring by another string
@param[in,out] s the string to manipulate; changed so that all
occurrences of @a f are replaced with @a t
@param[in] f the substring to replace with @a t
@param[in] t the string to replace @a f
@pre The search string @a f must not be empty. **This precondition is
enforced with an assertion.**
@since version 2.0.0
*/
inline void replace_substring(std::string& s, const std::string& f,
const std::string& t)
{
JSON_ASSERT(!f.empty());
for (auto pos = s.find(f); // find first occurrence of f
pos != std::string::npos; // make sure f was found
s.replace(pos, f.size(), t), // replace with t, and
pos = s.find(f, pos + t.size())) // find next occurrence of f
{}
}
/*!
* @brief string escaping as described in RFC 6901 (Sect. 4)
* @param[in] s string to escape
* @return escaped string
*
* Note the order of escaping "~" to "~0" and "/" to "~1" is important.
*/
inline std::string escape(std::string s)
{
replace_substring(s, "~", "~0");
replace_substring(s, "/", "~1");
return s;
}
/*!
* @brief string unescaping as described in RFC 6901 (Sect. 4)
* @param[in] s string to unescape
* @return unescaped string
*
* Note the order of escaping "~1" to "/" and "~0" to "~" is important.
*/
static void unescape(std::string& s)
{
replace_substring(s, "~1", "/");
replace_substring(s, "~0", "~");
}
} // namespace detail
} // namespace nlohmann

View File

@ -32,7 +32,7 @@ number_float), because the library distinguishes these three types for numbers:
@ref basic_json::number_float_t is used for floating-point numbers or to
approximate integers which do not fit in the limits of their respective type.
@sa @ref basic_json::basic_json(const value_t value_type) -- create a JSON
@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON
value with the default value for a given type
@since version 1.0.0

View File

@ -189,6 +189,7 @@ class basic_json
friend class ::nlohmann::detail::json_sax_dom_parser;
template<typename BasicJsonType>
friend class ::nlohmann::detail::json_sax_dom_callback_parser;
friend class ::nlohmann::detail::exception;
/// workaround type for MSVC
using basic_json_t = NLOHMANN_BASIC_JSON_TPL;
@ -1060,7 +1061,7 @@ class basic_json
object = nullptr; // silence warning, see #821
if (JSON_HEDLEY_UNLIKELY(t == value_t::null))
{
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1")); // LCOV_EXCL_LINE
JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.9.1", basic_json())); // LCOV_EXCL_LINE
}
break;
}
@ -1227,13 +1228,83 @@ class basic_json
invariant. Furthermore, it has to be called each time the type of a JSON
value is changed, because the invariant expresses a relationship between
@a m_type and @a m_value.
Furthermore, the parent relation is checked for arrays and objects: If
@a check_parents true and the value is an array or object, then the
container's elements must have the current value as parent.
@param[in] check_parents whether the parent relation should be checked.
The value is true by default and should only be set to false
during destruction of objects when the invariant does not
need to hold.
*/
void assert_invariant() const noexcept
void assert_invariant(bool check_parents = true) const noexcept
{
JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr);
JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr);
JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr);
JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr);
#if JSON_DIAGNOSTICS
JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j)
{
return j.m_parent == this;
}));
#else
static_cast<void>(check_parents);
#endif
}
void set_parents()
{
#if JSON_DIAGNOSTICS
switch (m_type)
{
case value_t::array:
{
for (auto& element : *m_value.array)
{
element.m_parent = this;
}
break;
}
case value_t::object:
{
for (auto& element : *m_value.object)
{
element.second.m_parent = this;
}
break;
}
default:
break;
}
#endif
}
iterator set_parents(iterator it, typename iterator::difference_type count)
{
#if JSON_DIAGNOSTICS
for (typename iterator::difference_type i = 0; i < count; ++i)
{
(it + i)->m_parent = this;
}
#else
static_cast<void>(count);
#endif
return it;
}
reference set_parent(reference j)
{
#if JSON_DIAGNOSTICS
j.m_parent = this;
#else
static_cast<void>(j);
#endif
return j;
}
public:
@ -1450,6 +1521,7 @@ class basic_json
std::forward<CompatibleType>(val))))
{
JSONSerializer<U>::to_json(*this, std::forward<CompatibleType>(val));
set_parents();
assert_invariant();
}
@ -1528,6 +1600,7 @@ class basic_json
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINE
}
set_parents();
assert_invariant();
}
@ -1629,7 +1702,7 @@ class basic_json
// if object is wanted but impossible, throw an exception
if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object))
{
JSON_THROW(type_error::create(301, "cannot create object from initializer list"));
JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json()));
}
}
@ -1639,13 +1712,13 @@ class basic_json
m_type = value_t::object;
m_value = value_t::object;
std::for_each(init.begin(), init.end(), [this](const detail::json_ref<basic_json>& element_ref)
for (auto& element_ref : init)
{
auto element = element_ref.moved_or_copied();
m_value.object->emplace(
std::move(*((*element.m_value.array)[0].m_value.string)),
std::move((*element.m_value.array)[1]));
});
}
}
else
{
@ -1654,6 +1727,7 @@ class basic_json
m_value.array = create<array_t>(init.begin(), init.end());
}
set_parents();
assert_invariant();
}
@ -1863,6 +1937,7 @@ class basic_json
: m_type(value_t::array)
{
m_value.array = create<array_t>(cnt, val);
set_parents();
assert_invariant();
}
@ -1932,7 +2007,7 @@ class basic_json
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible"));
JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json()));
}
// copy type from first iterator
@ -1950,7 +2025,7 @@ class basic_json
if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object));
}
break;
}
@ -2012,10 +2087,10 @@ class basic_json
}
default:
JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " +
std::string(first.m_object->type_name())));
JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object));
}
set_parents();
assert_invariant();
}
@ -2114,6 +2189,7 @@ class basic_json
break;
}
set_parents();
assert_invariant();
}
@ -2148,12 +2224,13 @@ class basic_json
m_value(std::move(other.m_value))
{
// check that passed value is valid
other.assert_invariant();
other.assert_invariant(false);
// invalidate payload
other.m_type = value_t::null;
other.m_value = {};
set_parents();
assert_invariant();
}
@ -2194,6 +2271,7 @@ class basic_json
swap(m_type, other.m_type);
swap(m_value, other.m_value);
set_parents();
assert_invariant();
return *this;
}
@ -2215,7 +2293,7 @@ class basic_json
*/
~basic_json() noexcept
{
assert_invariant();
assert_invariant(false);
m_value.destroy(m_type);
}
@ -2708,7 +2786,7 @@ class basic_json
return m_value.boolean;
}
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name())));
JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this));
}
/// get a pointer to the value (object)
@ -2829,7 +2907,7 @@ class basic_json
return *ptr;
}
JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name())));
JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj));
}
public:
@ -3257,7 +3335,7 @@ class basic_json
{
if (!is_binary())
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name())));
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
}
return *get_ptr<binary_t*>();
@ -3268,7 +3346,7 @@ class basic_json
{
if (!is_binary())
{
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name())));
JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this));
}
return *get_ptr<const binary_t*>();
@ -3318,17 +3396,17 @@ class basic_json
{
JSON_TRY
{
return m_value.array->at(idx);
return set_parent(m_value.array->at(idx));
}
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@ -3370,12 +3448,12 @@ class basic_json
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@ -3416,17 +3494,17 @@ class basic_json
{
JSON_TRY
{
return m_value.object->at(key);
return set_parent(m_value.object->at(key));
}
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@ -3472,12 +3550,12 @@ class basic_json
JSON_CATCH (std::out_of_range&)
{
// create better exception explanation
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found"));
JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this));
}
}
else
{
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name())));
JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this));
}
}
@ -3522,15 +3600,22 @@ class basic_json
// fill up array with null values if given idx is outside range
if (idx >= m_value.array->size())
{
m_value.array->insert(m_value.array->end(),
idx - m_value.array->size() + 1,
basic_json());
#if JSON_DIAGNOSTICS
// remember array size before resizing
const auto previous_size = m_value.array->size();
#endif
m_value.array->resize(idx + 1);
#if JSON_DIAGNOSTICS
// set parent for values added above
set_parents(begin() + static_cast<typename iterator::difference_type>(previous_size), static_cast<typename iterator::difference_type>(idx + 1 - previous_size));
#endif
}
return m_value.array->operator[](idx);
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
}
/*!
@ -3560,7 +3645,7 @@ class basic_json
return m_value.array->operator[](idx);
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this));
}
/*!
@ -3603,10 +3688,10 @@ class basic_json
// operator[] only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
return m_value.object->operator[](key);
return set_parent(m_value.object->operator[](key));
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@ -3648,7 +3733,7 @@ class basic_json
return m_value.object->find(key)->second;
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@ -3693,10 +3778,10 @@ class basic_json
// at only works for objects
if (JSON_HEDLEY_LIKELY(is_object()))
{
return m_value.object->operator[](key);
return set_parent(m_value.object->operator[](key));
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@ -3740,7 +3825,7 @@ class basic_json
return m_value.object->find(key)->second;
}
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name())));
JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this));
}
/*!
@ -3812,7 +3897,7 @@ class basic_json
return default_value;
}
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
}
/*!
@ -3885,7 +3970,7 @@ class basic_json
}
}
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name())));
JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this));
}
/*!
@ -4039,7 +4124,7 @@ class basic_json
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != pos.m_object))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
IteratorType result = end();
@ -4055,7 +4140,7 @@ class basic_json
{
if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin()))
{
JSON_THROW(invalid_iterator::create(205, "iterator out of range"));
JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this));
}
if (is_string())
@ -4091,7 +4176,7 @@ class basic_json
}
default:
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
return result;
@ -4152,7 +4237,7 @@ class basic_json
// make sure iterator fits the current value
if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object))
{
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value"));
JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this));
}
IteratorType result = end();
@ -4169,7 +4254,7 @@ class basic_json
if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin()
|| !last.m_it.primitive_iterator.is_end()))
{
JSON_THROW(invalid_iterator::create(204, "iterators out of range"));
JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this));
}
if (is_string())
@ -4207,7 +4292,7 @@ class basic_json
}
default:
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
return result;
@ -4250,7 +4335,7 @@ class basic_json
return m_value.object->erase(key);
}
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
/*!
@ -4284,14 +4369,14 @@ class basic_json
{
if (JSON_HEDLEY_UNLIKELY(idx >= size()))
{
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this));
}
m_value.array->erase(m_value.array->begin() + static_cast<difference_type>(idx));
}
else
{
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name())));
JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this));
}
}
@ -5236,7 +5321,7 @@ class basic_json
// push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@ -5249,6 +5334,7 @@ class basic_json
// add element to array (move semantics)
m_value.array->push_back(std::move(val));
set_parent(m_value.array->back());
// if val is moved from, basic_json move constructor marks it null so we do not call the destructor
}
@ -5271,7 +5357,7 @@ class basic_json
// push_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@ -5284,6 +5370,7 @@ class basic_json
// add element to array
m_value.array->push_back(val);
set_parent(m_value.array->back());
}
/*!
@ -5321,7 +5408,7 @@ class basic_json
// push_back only works for null objects or objects
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name())));
JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this));
}
// transform null object into an object
@ -5332,8 +5419,9 @@ class basic_json
assert_invariant();
}
// add element to array
m_value.object->insert(val);
// add element to object
auto res = m_value.object->insert(val);
set_parent(res.first->second);
}
/*!
@ -5424,7 +5512,7 @@ class basic_json
// emplace_back only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array())))
{
JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name())));
JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this));
}
// transform null object into an array
@ -5437,10 +5525,10 @@ class basic_json
// add element to array (perfect forwarding)
#ifdef JSON_HAS_CPP_17
return m_value.array->emplace_back(std::forward<Args>(args)...);
return set_parent(m_value.array->emplace_back(std::forward<Args>(args)...));
#else
m_value.array->emplace_back(std::forward<Args>(args)...);
return m_value.array->back();
return set_parent(m_value.array->back());
#endif
}
@ -5477,7 +5565,7 @@ class basic_json
// emplace only works for null objects or arrays
if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object())))
{
JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name())));
JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this));
}
// transform null object into an object
@ -5490,6 +5578,8 @@ class basic_json
// add element to array (perfect forwarding)
auto res = m_value.object->emplace(std::forward<Args>(args)...);
set_parent(res.first->second);
// create result iterator and set iterator to the result of emplace
auto it = begin();
it.m_it.object_iterator = res.first;
@ -5548,14 +5638,14 @@ class basic_json
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
return insert_iterator(pos, val);
return set_parents(insert_iterator(pos, val), static_cast<typename iterator::difference_type>(1));
}
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
/*!
@ -5599,14 +5689,14 @@ class basic_json
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
return insert_iterator(pos, cnt, val);
return set_parents(insert_iterator(pos, cnt, val), static_cast<typename iterator::difference_type>(cnt));
}
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
/*!
@ -5644,28 +5734,28 @@ class basic_json
// insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
if (JSON_HEDLEY_UNLIKELY(first.m_object == this))
{
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container"));
JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this));
}
// insert to array and return iterator
return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator);
return set_parents(insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator), std::distance(first, last));
}
/*!
@ -5697,17 +5787,17 @@ class basic_json
// insert only works for arrays
if (JSON_HEDLEY_UNLIKELY(!is_array()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if iterator pos fits to this JSON value
if (JSON_HEDLEY_UNLIKELY(pos.m_object != this))
{
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value"));
JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this));
}
// insert to array and return iterator
return insert_iterator(pos, ilist.begin(), ilist.end());
return set_parents(insert_iterator(pos, ilist.begin(), ilist.end()), static_cast<typename iterator::difference_type>(ilist.size()));
}
/*!
@ -5738,19 +5828,19 @@ class basic_json
// insert only works for objects
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name())));
JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
// passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()))
{
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this));
}
m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator);
@ -5787,11 +5877,11 @@ class basic_json
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name())));
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this));
}
for (auto it = j.cbegin(); it != j.cend(); ++it)
@ -5838,20 +5928,20 @@ class basic_json
if (JSON_HEDLEY_UNLIKELY(!is_object()))
{
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name())));
JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this));
}
// check if range iterators belong to the same JSON object
if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object))
{
JSON_THROW(invalid_iterator::create(210, "iterators do not fit"));
JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this));
}
// passed iterators must belong to objects
if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object()
|| !last.m_object->is_object()))
{
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects"));
JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this));
}
for (auto it = first; it != last; ++it)
@ -5886,6 +5976,9 @@ class basic_json
{
std::swap(m_type, other.m_type);
std::swap(m_value, other.m_value);
set_parents();
other.set_parents();
assert_invariant();
}
@ -5946,7 +6039,7 @@ class basic_json
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@ -5979,7 +6072,7 @@ class basic_json
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@ -6012,7 +6105,7 @@ class basic_json
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@ -6045,7 +6138,7 @@ class basic_json
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@ -6059,7 +6152,7 @@ class basic_json
}
else
{
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name())));
JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this));
}
}
@ -6967,6 +7060,11 @@ class basic_json
/// the value of the current element
json_value m_value = {};
#if JSON_DIAGNOSTICS
/// a pointer to a parent value (for debugging purposes)
basic_json* m_parent = nullptr;
#endif
//////////////////////////////////////////
// binary serialization/deserialization //
//////////////////////////////////////////
@ -8263,7 +8361,7 @@ class basic_json
if (JSON_HEDLEY_UNLIKELY(idx > parent.size()))
{
// avoid undefined behavior
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range"));
JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent));
}
// default case: insert add offset
@ -8279,7 +8377,7 @@ class basic_json
};
// wrapper for "remove" operation; remove value at ptr
const auto operation_remove = [&result](json_pointer & ptr)
const auto operation_remove = [this, &result](json_pointer & ptr)
{
// get reference to parent of JSON pointer ptr
const auto last_path = ptr.back();
@ -8297,7 +8395,7 @@ class basic_json
}
else
{
JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found"));
JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this));
}
}
else if (parent.is_array())
@ -8310,7 +8408,7 @@ class basic_json
// type check: top level value must be an array
if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array()))
{
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch));
}
// iterate and apply the operations
@ -8330,13 +8428,13 @@ class basic_json
// check if desired value is present
if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end()))
{
JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'"));
JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val));
}
// check if result is of type string
if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string()))
{
JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'"));
JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val));
}
// no error: return value
@ -8346,7 +8444,7 @@ class basic_json
// type check: every element of the array must be an object
if (JSON_HEDLEY_UNLIKELY(!val.is_object()))
{
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects"));
JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val));
}
// collect mandatory members
@ -8424,7 +8522,7 @@ class basic_json
// throw an exception if test fails
if (JSON_HEDLEY_UNLIKELY(!success))
{
JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump()));
JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val));
}
break;
@ -8434,7 +8532,7 @@ class basic_json
{
// op must be "add", "remove", "replace", "move", "copy", or
// "test"
JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid"));
JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val));
}
}
}
@ -8550,7 +8648,7 @@ class basic_json
for (auto it = source.cbegin(); it != source.cend(); ++it)
{
// escape the key name to be used in a JSON patch
const auto key = json_pointer::escape(it.key());
const auto key = detail::escape(it.key());
if (target.find(it.key()) != target.end())
{
@ -8574,7 +8672,7 @@ class basic_json
if (source.find(it.key()) == source.end())
{
// found a key that is not in this -> add it
const auto key = json_pointer::escape(it.key());
const auto key = detail::escape(it.key());
result.push_back(
{
{"op", "add"}, {"path", path + "/" + key},

File diff suppressed because it is too large Load Diff

View File

@ -110,6 +110,7 @@ set(files
src/unit-convenience.cpp
src/unit-conversions.cpp
src/unit-deserialization.cpp
src/unit-diagnostics.cpp
src/unit-element_access1.cpp
src/unit-element_access2.cpp
src/unit-hash.cpp

View File

@ -101,7 +101,11 @@ TEST_CASE("BSON")
{ std::string("en\0try", 6), true }
};
CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] (/en) BSON key cannot contain code point U+0000 (at byte 2)");
#else
CHECK_THROWS_WITH(json::to_bson(j), "[json.exception.out_of_range.409] BSON key cannot contain code point U+0000 (at byte 2)");
#endif
}
SECTION("string length must be at least 1")
@ -1235,7 +1239,11 @@ TEST_CASE("BSON numerical data")
};
CHECK_THROWS_AS(json::to_bson(j), json::out_of_range&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] (/entry) integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64");
#else
CHECK_THROWS_WITH_STD_STR(json::to_bson(j), "[json.exception.out_of_range.407] integer number " + std::to_string(i) + " cannot be represented by BSON as it does not fit int64");
#endif
}
}

View File

@ -0,0 +1,111 @@
/*
__ _____ _____ _____
__| | __| | | | JSON for Modern C++ (test suite)
| | |__ | | | | | | version 3.9.1
|_____|_____|_____|_|___| https://github.com/nlohmann/json
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "doctest_compatibility.h"
#ifdef JSON_DIAGNOSTICS
#undef JSON_DIAGNOSTICS
#endif
#define JSON_DIAGNOSTICS 1
#include <nlohmann/json.hpp>
using nlohmann::json;
TEST_CASE("Better diagnostics")
{
SECTION("empty JSON Pointer")
{
json j = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j.get<std::string>(), "[json.exception.type_error.302] type must be string, but is number", json::type_error);
}
SECTION("invalid type")
{
json j;
j["a"]["b"]["c"] = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j["a"]["b"]["c"].get<std::string>(), "[json.exception.type_error.302] (/a/b/c) type must be string, but is number", json::type_error);
}
SECTION("missing key")
{
json j;
j["object"]["object"] = true;
CHECK_THROWS_WITH_AS(j["object"].at("not_found"), "[json.exception.out_of_range.403] (/object) key 'not_found' not found", json::out_of_range);
}
SECTION("array index out of range")
{
json j;
j["array"][4] = true;
CHECK_THROWS_WITH_AS(j["array"].at(5), "[json.exception.out_of_range.401] (/array) array index 5 is out of range", json::out_of_range);
}
SECTION("array index at wrong type")
{
json j;
j["array"][4] = true;
CHECK_THROWS_WITH_AS(j["array"][4][5], "[json.exception.type_error.305] (/array/4) cannot use operator[] with a numeric argument with boolean", json::type_error);
}
SECTION("wrong iterator")
{
json j;
j["array"] = json::array();
CHECK_THROWS_WITH_AS(j["array"].erase(j.begin()), "[json.exception.invalid_iterator.202] (/array) iterator does not fit current value", json::invalid_iterator);
}
SECTION("JSON Pointer escaping")
{
json j;
j["a/b"]["m~n"] = 1;
std::string s;
CHECK_THROWS_WITH_AS(s = j["a/b"]["m~n"].get<std::string>(), "[json.exception.type_error.302] (/a~1b/m~0n) type must be string, but is number", json::type_error);
}
SECTION("Parse error")
{
CHECK_THROWS_WITH_AS(json::parse(""), "[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal", json::parse_error);
}
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562#pullrequestreview-574858448")
{
CHECK_THROWS_WITH_AS(json({"0", "0"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
CHECK_THROWS_WITH_AS(json({"0", "1"})[1].get<int>(), "[json.exception.type_error.302] (/1) type must be number, but is string", json::type_error);
}
SECTION("Regression test for https://github.com/nlohmann/json/pull/2562/files/380a613f2b5d32425021129cd1f371ddcfd54ddf#r563259793")
{
json j;
j["/foo"] = {1, 2, 3};
CHECK_THROWS_WITH_AS(j.unflatten(), "[json.exception.type_error.315] (/~1foo) values in object must be primitive", json::type_error);
}
}

View File

@ -90,6 +90,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -98,6 +108,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -124,6 +135,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -132,6 +153,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -159,6 +181,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -167,6 +199,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -194,6 +227,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -202,6 +245,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -227,13 +271,16 @@ TEST_CASE("iterators 2")
{
CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator&);
CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator&);
CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator&);
CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator&);
#if JSON_DIAGNOSTICS
// the output differs in each loop, so we cannot fix a string for the expected exception
#else
CHECK_THROWS_WITH(j.begin() == k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.begin() < k.begin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
#endif
}
}
}
@ -525,6 +572,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 < it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 < it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 < it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -533,6 +590,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c < it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c < it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -559,6 +617,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 <= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 <= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 <= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -567,6 +635,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c <= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c <= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -594,6 +663,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 > it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 > it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 > it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -602,6 +681,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c > it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c > it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -629,6 +709,16 @@ TEST_CASE("iterators 2")
CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator&);
CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator&);
CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it3, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it1_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] (/5) cannot compare order of object iterators");
#else
CHECK_THROWS_WITH(it1 >= it1, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1 >= it2, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2 >= it3, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
@ -637,6 +727,7 @@ TEST_CASE("iterators 2")
CHECK_THROWS_WITH(it1_c >= it2_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it2_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
CHECK_THROWS_WITH(it1_c >= it3_c, "[json.exception.invalid_iterator.213] cannot compare order of object iterators");
#endif
}
else
{
@ -662,13 +753,16 @@ TEST_CASE("iterators 2")
{
CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator&);
CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator&);
CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator&);
CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator&);
#if JSON_DIAGNOSTICS
// the output differs in each loop, so we cannot fix a string for the expected exception
#else
CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "[json.exception.invalid_iterator.212] cannot compare iterators of different containers");
#endif
}
}
}

View File

@ -343,7 +343,11 @@ TEST_CASE("JSON patch")
// check that evaluation throws
CHECK_THROWS_AS(doc.patch(patch), json::other_error&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump());
#else
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump());
#endif
}
SECTION("A.10. Adding a Nested Member Object")
@ -484,7 +488,11 @@ TEST_CASE("JSON patch")
// check that evaluation throws
CHECK_THROWS_AS(doc.patch(patch), json::other_error&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump());
#else
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump());
#endif
}
SECTION("A.16. Adding an Array Value")
@ -683,8 +691,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {"op", "add", "path", "", "value", 1};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.104] parse error: JSON patch must be an array of objects");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: (/0) JSON patch must be an array of objects");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.104] parse error: JSON patch must be an array of objects");
#endif
}
SECTION("missing 'op'")
@ -692,8 +703,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"foo", "bar"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation must have member 'op'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have member 'op'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have member 'op'");
#endif
}
SECTION("non-string 'op'")
@ -701,8 +715,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation must have string member 'op'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation must have string member 'op'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation must have string member 'op'");
#endif
}
SECTION("invalid operation")
@ -710,8 +727,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "foo"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation value 'foo' is invalid");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation value 'foo' is invalid");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation value 'foo' is invalid");
#endif
}
}
@ -722,8 +742,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "add"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -731,8 +754,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "add"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have string member 'path'");
#endif
}
SECTION("missing 'value'")
@ -740,8 +766,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "add"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'add' must have member 'value'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'add' must have member 'value'");
#endif
}
SECTION("invalid array index")
@ -761,8 +790,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "remove"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -770,8 +802,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "remove"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'remove' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'remove' must have string member 'path'");
#endif
}
SECTION("nonexisting target location (array)")
@ -809,8 +844,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "replace"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -818,8 +856,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "replace"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have string member 'path'");
#endif
}
SECTION("missing 'value'")
@ -827,8 +868,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "replace"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'replace' must have member 'value'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'replace' must have member 'value'");
#endif
}
SECTION("nonexisting target location (array)")
@ -857,8 +901,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "move"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -866,8 +913,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "move"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'path'");
#endif
}
SECTION("missing 'from'")
@ -875,8 +925,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "move"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have member 'from'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have member 'from'");
#endif
}
SECTION("non-string 'from'")
@ -884,8 +937,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'move' must have string member 'from'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'move' must have string member 'from'");
#endif
}
SECTION("nonexisting from location (array)")
@ -914,8 +970,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "copy"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -923,8 +982,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "copy"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'path'");
#endif
}
SECTION("missing 'from'")
@ -932,8 +994,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "copy"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have member 'from'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have member 'from'");
#endif
}
SECTION("non-string 'from'")
@ -941,8 +1006,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'copy' must have string member 'from'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'copy' must have string member 'from'");
#endif
}
SECTION("nonexisting from location (array)")
@ -971,8 +1039,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "test"}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'path'");
#endif
}
SECTION("non-string 'path'")
@ -980,8 +1051,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "test"}, {"path", 1}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have string member 'path'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have string member 'path'");
#endif
}
SECTION("missing 'value'")
@ -989,8 +1063,11 @@ TEST_CASE("JSON patch")
json j;
json patch = {{{"op", "test"}, {"path", ""}}};
CHECK_THROWS_AS(j.patch(patch), json::parse_error&);
CHECK_THROWS_WITH(j.patch(patch),
"[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: (/0) operation 'test' must have member 'value'");
#else
CHECK_THROWS_WITH(j.patch(patch), "[json.exception.parse_error.105] parse error: operation 'test' must have member 'value'");
#endif
}
}
}
@ -1183,7 +1260,11 @@ TEST_CASE("JSON patch")
// the test will fail
CHECK_THROWS_AS(doc.patch(patch), json::other_error&);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] (/0) unsuccessful: " + patch[0].dump());
#else
CHECK_THROWS_WITH_STD_STR(doc.patch(patch), "[json.exception.other_error.501] unsuccessful: " + patch[0].dump());
#endif
}
}
}

View File

@ -496,8 +496,11 @@ TEST_CASE("JSON pointers")
// error for nonprimitve values
CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), json::type_error&);
CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(),
"[json.exception.type_error.315] values in object must be primitive");
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] (/~11) values in object must be primitive");
#else
CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "[json.exception.type_error.315] values in object must be primitive");
#endif
// error for conflicting values
json j_error = {{"", 42}, {"/foo", 17}};

View File

@ -394,7 +394,11 @@ TEST_CASE("regression tests 1")
// improve coverage
o["int"] = 1;
CHECK_THROWS_AS(s2 = o["int"], json::type_error);
#if JSON_DIAGNOSTICS
CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] (/int) type must be string, but is number");
#else
CHECK_THROWS_WITH(s2 = o["int"], "[json.exception.type_error.302] type must be string, but is number");
#endif
}
#endif

View File

@ -1181,8 +1181,8 @@ TEST_CASE("Unicode" * doctest::skip())
CHECK_NOTHROW(json::json_pointer("/" + ptr));
// check escape/unescape roundtrip
auto escaped = json::json_pointer::escape(ptr);
json::json_pointer::unescape(escaped);
auto escaped = nlohmann::detail::escape(ptr);
nlohmann::detail::unescape(escaped);
CHECK(escaped == ptr);
}
}