mirror of
https://github.com/nlohmann/json.git
synced 2026-10-25 15:28:54 +00:00
* Add shared detail helpers for the #5891 cleanup Add detail::is_primitive(value_t), detail::hex4_lower(), detail::utf8_shape(), detail::input_format_name(), detail::empty_input_message(), the exception ids 116, 319, 320 and 416 to detail::exception_id, and make detail::is_ascii_copyable() constexpr. Later commits switch the duplicated code over to them. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.4: BSON writers share one write-pass UTF-8 sanitizer (#5891) write_bson_entry_header and write_bson_string re-derived the error-handler policy differently. Both now use sanitize_utf8_for_bson_write, which treats strict like keep (the size pass already validated) and sanitizes replace/ignore into the bytes the size pass measured. No context plumbing is needed. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.1: one MessagePack container prefix helper (#5891) Array and object prefixes differed only in the fix marker and the 16/32-bit markers; both now delegate to write_msgpack_container_prefix. Output is byte-identical. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.2: one iterative walker for CBOR and MessagePack (#5891) write_cbor_iterative and write_msgpack_iterative (and their *_value_or_push helpers) were identical but for the head, scalar, and key writers. They are now one template over a small policy struct, so everything inlines. BON8 keeps its own walker (open string run, closing marker), noted in a comment. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.3, 2.1.8: one UBJSON container start helper, shared frame base (#5891) write_ubjson_start_array/_object are now write_ubjson_start_container<IsArray>, keeping the array-only begin()+1 scan and excessive_valueless rule. ubjson_frame derives from binary_container_frame instead of duplicating it. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.4: write_bytes helper for the byte-string write sites (#5891) The 13 oa.write_characters(reinterpret_cast<const CharType*>(x.data()), n) sites now call write_bytes(const void*, size_t); copy_characters sites and the buffer-based writes are untouched. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.1.5: shared object-key writer for CBOR and MessagePack (#5891) write_cbor_key and write_msgpack_key were the same SFINAE pair apart from the string/scalar writers; both now delegate to write_object_key<Walk>. UBJSON key and to_format_length stay as they are. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.10: writer-side format names from input_format_name (#5891) The CBOR/MessagePack/UBJSON/BSON/BJData/BON8 literals in throw_on_discarded and throw_integer_out_of_range calls now come from detail::input_format_name; the texts are identical, and UBJSON vs BJData still follows use_bjdata. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * serializer: constexpr ascii table, hex4_lower, dedupe helpers (#5891) 1.1: build ascii_copyable() at compile time from is_ascii_copyable and test find_ascii_copyable_run against it for every byte. 1.2/2.5.4: use detail::hex4_lower in both \uXXXX writers. 2.1.6: factor the repeated flush/emit blocks in dump_escaped_impl into always-inline helpers. 2.1.7: comment why the escape copy does not use put_buffer. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.6: use string_t-aware to_string/concat in meta() (#5891) Replaces the four std::to_string sites; rendered text unchanged. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: use detail::is_primitive in json.hpp switches (#5891) has_no_children, set_parents, max_size and the two diff walkers. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.1: factor insert()/iterator throw helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.2: share swap() overloads via swap_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.4: factor primitive reset and range check in erase()/range ctor (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.6: share push_back array bodies via push_back_array_impl (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.7: route parse/accept/sax_parse through *_impl helpers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.8: share the vector-returning to_* bodies via to_binary_vector (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.2.10: merge destroy_string/destroy_binary into destroy_pointer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.1/1.7: exceptions: share what_impl, flatten diagnostics switch (#5891) The four create() bodies build their message through one protected static helper; the diagnostics parent walk handles array/object and ignores all other types. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.2/2.4.3/1.7: json_pointer: share slash and escape checks, drop primitive case lists (#5891) The leading-slash and invalid '~' escape checks are shared helpers; the reference-token walks test object and array first and treat everything else as primitive. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7/2.4.9: iter_impl, iteration_proxy: drop primitive case lists, share compare preamble (#5891) Object and array stay the first branches; everything else is the primitive case. operator== and operator< share the different-container check. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5/2.4.6: from_json: share get_number_value and reserve_if_possible (#5891) The three number cases of get_arithmetic_value and the arithmetic from_json share one helper (boolean stays accepted only by the latter); the array and object reserve overload pairs become one. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.8: ordered_map: share erase_impl between the key erase overloads (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.7: json_pointer: flatten walk drops the primitive case list (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.2/1.3/1.5/2.5.1/2.5.2/2.5.5: share helpers in the view code (#5891) - detail::exception_id at every numeric id; throw_* helpers take it - hex_byte, hex4_lower, utf8_shape replace local hex/table code - throw_array_index_out_of_range/_past_end, throw_key_not_found - has_native_float_format replaces the hand-written IEEE check No change of messages, ids or output. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: use utf8_shape in get_bon8_string, add exhaustive UTF-8 validator test (#5891) get_bon8_string takes the valid range of the second byte from utf8_shape() (0xF5..0xF7 stay errors, unget behavior unchanged). validate_one_utf8 keeps its if-chain (hot bulk path); the new test checks decode, validate_one_utf8, utf8_shape, json::accept and the BON8 reader against each other for every lead and second byte. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.1: get_cbor_sized_bytes for the CBOR chunk getters (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.2: get_string_with_length for the plain-order string sites (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.3, 2.3.4, 2.3.10: unsupported_key_type, empty_input_message, input_format_name in the readers (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.7, 2.3.9: read_object_key and leave_container_and_check for CBOR/MessagePack/BON8 (#5891) UBJSON keys (different getter signature) and its container tail stay as is. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.5: json_sax_dom_error_state base for the DOM SAX parsers (#5891) Holds errored, allow_exceptions, parse_error() and is_errored(). The scalar handlers stay in each class (parse hot path). Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.6: table of control-character messages in the lexer (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.3.8: finish_input helper for the strict-EOF block in the parser (#5891) sax_parse keeps its if (result) guard and return value. Also adds the clang-tidy NOLINT for the 2.3.6 message table. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: share parse_array_index between json_pointer and json_view (#5891) Move json_pointer::parse_array_index() and its status enum to namespace detail (json_pointer keeps a forwarding member), and let the json_view pointer resolution use it instead of its own copy of the RFC 6901 index grammar. Both accepted the same tokens with the same outcome. Also update the comment on detail::decode(), which listed four of the five UTF-8 validators and now points to the test that keeps them in sync. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.4.5: keep the inlined number cases in from_json(ArithmeticType&) (#5891) Calling get_number_value() from from_json(ArithmeticType&) made GetArray<std::array<int, 1000>> 2.4x slower (it changes the inlining of the std::array conversion), so that overload keeps its own switch, with a comment. get_arithmetic_value() still uses the shared helper. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 2.5.7: force-inline detail::parse_array_index (#5891) Moving parse_array_index out of json_pointer stopped clang from inlining it into the pointer resolution (JsonPointerAt/citm_catalog +17%, JsonPointerAt/twitter +8%); JSON_HEDLEY_ALWAYS_INLINE restores the baseline. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * 1.5: keep get_bon8_string's own UTF-8 ranges (#5891) Taking the second-byte range from utf8_shape() made BON8 decoding 2-3% slower (FromBinaryShape/scalars, FromBinaryBuffer, alignment-pinned builds), so the byte path keeps its hand-written ranges. The exhaustive validator test still checks that it accepts the same sequences as the other validators. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Amalgamate (#5891) Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Fix CI failures: constexpr, NRVO, cppcheck, IWYU (#5891) - json_sax_dom_error_state::is_errored() cannot be constexpr: the class is not a literal type (GCC 4.8, GCC -Wpedantic, Clang 3.5). - to_binary_vector: destroy the vector writer before returning, as the output_vector_sink sets the size of the vector in its destructor. Without NRVO (MSVC Debug in C++14 mode), the vector was moved out before it was resized. - parser::sax_parse: return false instead of result after the early exit (cppcheck identicalConditionAfterEarlyExit). - json.hpp: export detail/exceptions.hpp for IWYU, so json_view.hpp can name exception_id without including a detail header. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Address review: throw helpers take input_format_t, simpler type checks - throw_on_discarded() and throw_integer_out_of_range() take an input_format_t and call input_format_name() themselves - has_no_children(), set_parents() and max_size() check array, then object, instead of calling is_primitive() first - parse_impl() chains like accept_impl(); sax_parse_impl() uses if/else - std::array for the control character messages; negation<> in write_object_key(); clarify the is_primitive() comment Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_view: use exception_id in the value() overloads Signed-off-by: Niels Lohmann <mail@nlohmann.me> * json_sax: assert the invariant of place_container for the static analyzer Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
526 lines
24 KiB
C++
526 lines
24 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <cstddef> // nullptr_t
|
|
#include <exception> // exception
|
|
#if JSON_DIAGNOSTICS
|
|
#include <numeric> // accumulate
|
|
#endif
|
|
#include <stdexcept> // runtime_error
|
|
#include <string> // to_string
|
|
#include <vector> // vector
|
|
|
|
#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>
|
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
#include <nlohmann/detail/string_concat.hpp>
|
|
|
|
// With -Wweak-vtables, Clang will complain about the exception classes as they
|
|
// have no out-of-line virtual method definitions and their vtable will be
|
|
// emitted in every translation unit. This issue cannot be fixed with a
|
|
// header-only library as there is no implementation file to move these
|
|
// functions to. As a result, we suppress this warning here to avoid client
|
|
// code stumbling over this. See https://github.com/nlohmann/json/issues/4087
|
|
// for a discussion.
|
|
#if defined(__clang__)
|
|
JSON_HEDLEY_DIAGNOSTIC_PUSH
|
|
JSON_HEDLEY_PRAGMA(clang diagnostic ignored "-Wweak-vtables")
|
|
#endif
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
|
|
////////////////
|
|
// exceptions //
|
|
////////////////
|
|
|
|
/*!
|
|
@brief the ids of the exceptions thrown by the library
|
|
@note The values are part of the public API: they are the `id` member of the
|
|
exceptions and appear in their `what()` messages.
|
|
@sa https://json.nlohmann.me/home/exceptions/
|
|
*/
|
|
enum class exception_id : int
|
|
{
|
|
// parse_error
|
|
syntax_error = 101, ///< unexpected token or invalid literal while parsing JSON
|
|
patch_not_an_array = 104, ///< a JSON Patch document is not an array of objects
|
|
patch_invalid_operation = 105, ///< a JSON Patch operation is malformed
|
|
pointer_index_leading_zero = 106, ///< a JSON Pointer array index has a leading zero
|
|
pointer_missing_slash = 107, ///< a JSON Pointer does not start with '/'
|
|
pointer_invalid_escape = 108, ///< a JSON Pointer contains an escape other than ~0 and ~1
|
|
pointer_index_not_a_number = 109, ///< a JSON Pointer array index is not a number
|
|
unexpected_end_of_input = 110, ///< a binary input ends early, or has bytes left at its end
|
|
unexpected_byte = 112, ///< a binary input contains an unexpected byte or invalid length
|
|
invalid_string_or_size = 113, ///< a binary input contains an invalid string or size specification
|
|
bson_unsupported_type = 114, ///< a BSON record type is not supported
|
|
invalid_high_precision_number = 115, ///< a UBJSON/BJData high-precision number cannot be parsed
|
|
invalid_document_image = 116, ///< json_document::load() rejects an image
|
|
// invalid_iterator
|
|
iterators_incompatible = 201, ///< the iterators of a range belong to different values
|
|
iterator_from_other_value = 202, ///< an iterator does not belong to the value it is used with
|
|
iterator_range_from_other_value = 203, ///< an iterator range does not belong to the value it is used with
|
|
iterator_range_out_of_range = 204, ///< an iterator range of a primitive value is not [begin, end)
|
|
iterator_out_of_range = 205, ///< an iterator of a primitive value is not begin()
|
|
iterator_range_of_null = 206, ///< an iterator range belongs to a null value
|
|
iterator_key_not_object = 207, ///< key() is called on an iterator of a non-object
|
|
iterator_subscript_on_object = 208, ///< operator[] is called on an iterator of an object
|
|
iterator_arithmetic_on_object = 209, ///< an offset operator is used on an iterator of an object
|
|
insert_range_incompatible = 210, ///< the iterators of an inserted range belong to different values
|
|
insert_range_into_itself = 211, ///< an inserted range belongs to the value it is inserted into
|
|
iterators_compare_different_values = 212, ///< iterators of different values are compared
|
|
iterator_order_on_object = 213, ///< iterators of an object are compared by order
|
|
iterator_value_unavailable = 214, ///< an iterator does not refer to a value
|
|
// type_error
|
|
object_from_non_pairs = 301, ///< an object is created from an initializer list that is not a list of pairs
|
|
type_mismatch = 302, ///< a value has the wrong type for a conversion
|
|
incompatible_reference_type = 303, ///< get_ref() is called with a reference type that does not match the value
|
|
at_wrong_type = 304, ///< at() is called on a value of the wrong type
|
|
subscript_wrong_type = 305, ///< operator[] is called on a value of the wrong type
|
|
value_wrong_type = 306, ///< value() is called on a value of the wrong type
|
|
erase_wrong_type = 307, ///< erase() is called on a value of the wrong type
|
|
push_back_wrong_type = 308, ///< push_back() or operator+= is called on a value of the wrong type
|
|
insert_wrong_type = 309, ///< insert() is called on a value of the wrong type
|
|
swap_wrong_type = 310, ///< swap() is called on a value of the wrong type
|
|
emplace_wrong_type = 311, ///< emplace() or emplace_back() is called on a value of the wrong type
|
|
update_wrong_type = 312, ///< update() is called on a value of the wrong type
|
|
unflatten_invalid_value = 313, ///< unflatten() finds conflicting paths
|
|
unflatten_not_object = 314, ///< unflatten() is called on a non-object
|
|
unflatten_value_not_primitive = 315, ///< unflatten() is called on an object with non-primitive values
|
|
invalid_utf8 = 316, ///< dump() finds a string that is not valid UTF-8
|
|
type_not_serializable = 317, ///< a value cannot be serialized to the requested binary format
|
|
enum_key_duplicate = 318, ///< an enum-keyed map has two keys that serialize to the same string
|
|
document_binary_value = 319, ///< a binary value is stored in a json_document
|
|
document_image_unsupported = 320, ///< a json_document image cannot be saved or loaded (discarded document, big-endian target)
|
|
discarded_value_used = 321, ///< a discarded value is used
|
|
// out_of_range
|
|
array_index_out_of_range = 401, ///< an array index is out of range
|
|
pointer_past_the_end_index = 402, ///< a JSON Pointer uses the array index '-'
|
|
key_not_found = 403, ///< an object key is not found
|
|
pointer_unresolved = 404, ///< a JSON Pointer reference token cannot be resolved
|
|
patch_on_root = 405, ///< the root of a value has no parent, e.g. for a JSON Patch 'remove' or 'add' at the root
|
|
number_overflow = 406, ///< a number cannot be stored without overflowing to NaN or INF
|
|
integer_too_large = 407, ///< an integer cannot be represented in the binary format
|
|
container_too_large = 408, ///< the size of a container in a binary input exceeds the maximal capacity
|
|
bson_key_with_null = 409, ///< a BSON key contains U+0000
|
|
value_out_of_range = 410, ///< an enum value is undefined, or a JSON Pointer array index exceeds size_type
|
|
patch_add_parent_not_container = 411, ///< the parent of a JSON Patch 'add' target is not a container
|
|
length_too_large = 412, ///< a length does not fit into the length field of a binary format
|
|
patch_remove_parent_not_container = 413, ///< the parent of a JSON Patch 'remove' target is not a container
|
|
patch_move_into_child = 414, ///< a JSON Patch 'move' moves a value into one of its children
|
|
subtype_out_of_range = 415, ///< a binary subtype does not fit into one byte
|
|
document_too_large = 416, ///< a json_document input, its edit storage, or an image reaches the 32-bit (4 GiB) limit
|
|
// other_error
|
|
internal_error = 500, ///< unreachable code was reached
|
|
patch_test_failed = 501, ///< a JSON Patch 'test' operation failed
|
|
size_marker_required = 502 ///< UBJSON/BJData output with type markers requires size markers
|
|
};
|
|
|
|
/// @brief general exception of the @ref basic_json class
|
|
/// @sa https://json.nlohmann.me/api/basic_json/exception/
|
|
class exception : public std::exception
|
|
{
|
|
public:
|
|
/// returns the explanatory string
|
|
const char* what() const noexcept override
|
|
{
|
|
return m.what();
|
|
}
|
|
|
|
/// the id of the exception
|
|
const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes)
|
|
|
|
protected:
|
|
JSON_HEDLEY_NON_NULL(3)
|
|
exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing)
|
|
|
|
static std::string name(const std::string& ename, int id_)
|
|
{
|
|
return concat("[json.exception.", ename, '.', std::to_string(id_), "] ");
|
|
}
|
|
|
|
/// build the message "[json.exception.<ename>.<id>] <diagnostics><what_arg>"
|
|
template<typename BasicJsonContext>
|
|
static std::string what_impl(const std::string& ename, int id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return concat(name(ename, id_), diagnostics(context), what_arg);
|
|
}
|
|
|
|
static std::string diagnostics(std::nullptr_t /*leaf_element*/)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
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 != nullptr && current->m_parent != nullptr; current = current->m_parent)
|
|
{
|
|
const auto* parent = current->m_parent;
|
|
if (parent->is_array())
|
|
{
|
|
for (std::size_t i = 0; i < parent->m_data.m_value.array->size(); ++i)
|
|
{
|
|
if (&parent->m_data.m_value.array->operator[](i) == current)
|
|
{
|
|
tokens.emplace_back(std::to_string(i));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (parent->is_object())
|
|
{
|
|
for (const auto& element : *parent->m_data.m_value.object)
|
|
{
|
|
if (&element.second == current)
|
|
{
|
|
// data() is null-terminated, so a key containing
|
|
// a null byte is cut short here rather than
|
|
// truncating the whole message at what()
|
|
tokens.emplace_back(element.first.data());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// all other parent types (primitives) contribute no token
|
|
}
|
|
|
|
if (tokens.empty())
|
|
{
|
|
return "";
|
|
}
|
|
|
|
auto str = std::accumulate(tokens.rbegin(), tokens.rend(), std::string{},
|
|
[](const std::string & a, const std::string & b)
|
|
{
|
|
return concat(a, '/', detail::escape(b));
|
|
});
|
|
|
|
return concat('(', str, ") ", get_byte_positions(leaf_element));
|
|
#else
|
|
return get_byte_positions(leaf_element);
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
/// an exception object as storage for error messages
|
|
std::runtime_error m;
|
|
#if JSON_DIAGNOSTIC_POSITIONS
|
|
template<typename BasicJsonType>
|
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
|
{
|
|
if ((leaf_element->start_pos() != std::string::npos) && (leaf_element->end_pos() != std::string::npos))
|
|
{
|
|
return concat("(bytes ", std::to_string(leaf_element->start_pos()), "-", std::to_string(leaf_element->end_pos()), ") ");
|
|
}
|
|
return "";
|
|
}
|
|
#else
|
|
template<typename BasicJsonType>
|
|
static std::string get_byte_positions(const BasicJsonType* leaf_element)
|
|
{
|
|
static_cast<void>(leaf_element);
|
|
return "";
|
|
}
|
|
#endif
|
|
};
|
|
|
|
/// @brief exception indicating a parse error
|
|
/// @sa https://json.nlohmann.me/api/basic_json/parse_error/
|
|
class parse_error : public exception
|
|
{
|
|
public:
|
|
/*!
|
|
@brief create a parse error exception
|
|
@param[in] id_ the id of the exception
|
|
@param[in] pos the position where the error occurred (or with
|
|
chars_read_total=0 if the position cannot be
|
|
determined)
|
|
@param[in] what_arg the explanatory string
|
|
@return parse_error object
|
|
*/
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static parse_error create(int id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = concat(exception::name("parse_error", id_), "parse error",
|
|
position_string(pos), ": ", exception::diagnostics(context), what_arg);
|
|
return {id_, pos.chars_read_total, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = concat(exception::name("parse_error", id_), "parse error",
|
|
(byte_ != 0 ? (concat(" at byte ", std::to_string(byte_))) : ""),
|
|
": ", exception::diagnostics(context), what_arg);
|
|
return {id_, byte_, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static parse_error create(exception_id id_, const position_t& pos, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), pos, what_arg, context);
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static parse_error create(exception_id id_, std::size_t byte_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), byte_, what_arg, context);
|
|
}
|
|
|
|
/*!
|
|
@brief byte index of the parse error
|
|
|
|
The byte index of the last read character in the input file.
|
|
|
|
@note For an input with n bytes, 1 is the index of the first character and
|
|
n+1 is the index of the terminating null byte or the end of file.
|
|
This also holds true when reading a byte vector (CBOR or MessagePack).
|
|
*/
|
|
const std::size_t byte;
|
|
|
|
private:
|
|
parse_error(int id_, std::size_t byte_, const char* what_arg)
|
|
: exception(id_, what_arg), byte(byte_) {}
|
|
|
|
static std::string position_string(const position_t& pos)
|
|
{
|
|
return concat(" at line ", std::to_string(pos.lines_read + 1),
|
|
", column ", std::to_string(pos.chars_read_current_line));
|
|
}
|
|
};
|
|
|
|
/// @brief exception indicating errors with iterators
|
|
/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/
|
|
class invalid_iterator : public exception
|
|
{
|
|
public:
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static invalid_iterator create(int id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = exception::what_impl("invalid_iterator", id_, what_arg, context);
|
|
return {id_, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static invalid_iterator create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), what_arg, context);
|
|
}
|
|
|
|
private:
|
|
JSON_HEDLEY_NON_NULL(3)
|
|
invalid_iterator(int id_, const char* what_arg)
|
|
: exception(id_, what_arg) {}
|
|
};
|
|
|
|
/// @brief exception indicating executing a member function with a wrong type
|
|
/// @sa https://json.nlohmann.me/api/basic_json/type_error/
|
|
class type_error : public exception
|
|
{
|
|
public:
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static type_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = exception::what_impl("type_error", id_, what_arg, context);
|
|
return {id_, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static type_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), what_arg, context);
|
|
}
|
|
|
|
private:
|
|
JSON_HEDLEY_NON_NULL(3)
|
|
type_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
};
|
|
|
|
/// @brief exception indicating access out of the defined range
|
|
/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/
|
|
class out_of_range : public exception
|
|
{
|
|
public:
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static out_of_range create(int id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = exception::what_impl("out_of_range", id_, what_arg, context);
|
|
return {id_, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static out_of_range create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), what_arg, context);
|
|
}
|
|
|
|
private:
|
|
JSON_HEDLEY_NON_NULL(3)
|
|
out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
};
|
|
|
|
/// @brief exception indicating other library errors
|
|
/// @sa https://json.nlohmann.me/api/basic_json/other_error/
|
|
class other_error : public exception
|
|
{
|
|
public:
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static other_error create(int id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
const std::string w = exception::what_impl("other_error", id_, what_arg, context);
|
|
return {id_, w.c_str()};
|
|
}
|
|
|
|
template<typename BasicJsonContext, enable_if_t<is_basic_json_context<BasicJsonContext>::value, int> = 0>
|
|
static other_error create(exception_id id_, const std::string& what_arg, BasicJsonContext context)
|
|
{
|
|
return create(static_cast<int>(id_), what_arg, context);
|
|
}
|
|
|
|
private:
|
|
JSON_HEDLEY_NON_NULL(3)
|
|
other_error(int id_, const char* what_arg) : exception(id_, what_arg) {}
|
|
};
|
|
|
|
/*!
|
|
@brief helper function to call JSON_THROW from a template
|
|
@note JSON_THROW is a macro that, depending on the JSON_THROW_USER /
|
|
JSON_TRY_USER / JSON_NOEXCEPTION configuration, may expand to code
|
|
that does not reference its argument (e.g. `std::abort()`), which
|
|
would trigger a compilation error if the argument's type depends on
|
|
a template parameter that is otherwise unused. Wrapping the call in
|
|
a templated function avoids this and gives the compiler a single
|
|
place to see the (possibly unused) parameter.
|
|
*/
|
|
template<typename ExceptionType>
|
|
void templated_json_throw(ExceptionType exception)
|
|
{
|
|
JSON_THROW(exception);
|
|
|
|
// JSON_THROW may expand to code that discards its argument (e.g. when
|
|
// exceptions are disabled) - the cast below avoids an unused-parameter
|
|
// warning with -Werror in that case
|
|
(void)exception;
|
|
}
|
|
|
|
/*!
|
|
@brief throws because @a j does not have the type a conversion expects
|
|
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
|
|
@param[in] j the value with the wrong type
|
|
@throw type_error.302 always
|
|
*/
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const char* expected, const BasicJsonType& j)
|
|
{
|
|
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
|
|
static_cast<void>(j);
|
|
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
|
|
}
|
|
|
|
/*!
|
|
@brief throws because an operation is not supported for the type of @a j
|
|
@param[in] id_ the id of the type_error exception (at_wrong_type..update_wrong_type)
|
|
@param[in] operation the operation, e.g. "erase()"
|
|
@param[in] j the value the operation was called on
|
|
@throw type_error always
|
|
*/
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_cannot_use_with(const exception_id id_, const char* operation, const BasicJsonType& j)
|
|
{
|
|
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
|
static_cast<void>(operation);
|
|
static_cast<void>(j);
|
|
JSON_THROW(type_error::create(id_, concat("cannot use ", operation, " with ", j.type_name()), &j));
|
|
}
|
|
|
|
/*!
|
|
@brief throws ExceptionType with a fixed message; kept out of line so that callers do not carry the exception construction
|
|
*/
|
|
template<typename ExceptionType, typename BasicJsonContext>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_literal(const exception_id id_, const char* what_arg, BasicJsonContext context)
|
|
{
|
|
static_cast<void>(id_); // unused when JSON_NOEXCEPTION is defined
|
|
static_cast<void>(what_arg);
|
|
static_cast<void>(context);
|
|
JSON_THROW(ExceptionType::create(id_, what_arg, context));
|
|
}
|
|
|
|
// JSON Patch errors; constructed out of line so that patch_inplace() does not carry them
|
|
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_add_parent(const BasicJsonType& parent)
|
|
{
|
|
static_cast<void>(parent);
|
|
JSON_THROW(out_of_range::create(exception_id::patch_add_parent_not_container, concat("cannot add value: the JSON Patch 'add' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_remove_parent(const BasicJsonType& parent)
|
|
{
|
|
static_cast<void>(parent);
|
|
JSON_THROW(out_of_range::create(exception_id::patch_remove_parent_not_container, concat("cannot remove value: the JSON Patch 'remove' target's parent is of type ", parent.type_name(), ", but must be an object or array"), &parent));
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_not_an_array(const BasicJsonType& j)
|
|
{
|
|
static_cast<void>(j);
|
|
JSON_THROW(parse_error::create(exception_id::patch_not_an_array, 0, "JSON patch must be an array of objects", &j));
|
|
}
|
|
|
|
template<typename StringType, typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_missing_member(const StringType& op, const StringType& member, const bool string_type, const BasicJsonType& val)
|
|
{
|
|
static_cast<void>(op);
|
|
static_cast<void>(member);
|
|
static_cast<void>(string_type);
|
|
static_cast<void>(val);
|
|
// context-sensitive error message
|
|
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat((op == "op") ? std::string("operation") : concat("operation '", op, '\''), string_type ? " must have string member '" : " must have member '", member, "'"), &val));
|
|
}
|
|
|
|
template<typename StringType, typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_move_into_child(const StringType& from_path, const StringType& path, const BasicJsonType& result)
|
|
{
|
|
static_cast<void>(from_path);
|
|
static_cast<void>(path);
|
|
static_cast<void>(result);
|
|
JSON_THROW(out_of_range::create(exception_id::patch_move_into_child, concat("cannot move value: 'from' path '", from_path, "' is a proper prefix of 'path' '", path, "'"), &result));
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_test_failed(const BasicJsonType& val)
|
|
{
|
|
static_cast<void>(val);
|
|
JSON_THROW(other_error::create(exception_id::patch_test_failed, concat("unsuccessful: ", val.dump()), &val));
|
|
}
|
|
|
|
template<typename StringType, typename BasicJsonType>
|
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_patch_invalid_operation(const StringType& op, const BasicJsonType& val)
|
|
{
|
|
static_cast<void>(op);
|
|
static_cast<void>(val);
|
|
JSON_THROW(parse_error::create(exception_id::patch_invalid_operation, 0, concat("operation value '", op, "' is invalid"), &val));
|
|
}
|
|
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|
|
|
|
#if defined(__clang__)
|
|
JSON_HEDLEY_DIAGNOSTIC_POP
|
|
#endif
|