mirror of
https://github.com/nlohmann/json.git
synced 2026-10-31 01:45:43 +00:00
With JSON_DIAGNOSTICS and JSON_DIAGNOSTIC_POSITIONS, the exceptions of views now carry the same context as those of basic_json: "(/path) " and "(bytes a-b) " in front of the message, in basic_json's format (with JSON_DIAGNOSTICS, no context for the root). Before, views threw without any context, and since get<T>() reads the view directly, conversions had lost even the relative path they used to get through materialize(). The throw helpers of detail/view/errors.hpp take an error_site (document and node; empty without the two macros). Only on the error path, the path is found by descending from the root (pruned by subtree extent before the first edit, a full walk through edited sequences after), and the bytes as materialize() sets them (position_source; a container's end by following the chain of last elements and scanning to the closing brackets). from_json() for views and the strict enum macro put the same context in front through exception_prefix(). Iterators keep their container only under the two macros, for key(). Without the macros, the accessors compile to the same code as before. Tests compare every value's exceptions with those of the parsed or materialized basic_json value, including edited documents; the checks that were disabled under the two macros run again. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
139 lines
4.8 KiB
C++
139 lines
4.8 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> // size_t
|
|
#include <cstdint> // uint64_t
|
|
#include <limits> // numeric_limits
|
|
#include <string> // string, to_string
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/detail/view/errors.hpp>
|
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
namespace view
|
|
{
|
|
|
|
/// what resolving a JSON pointer does where it cannot continue
|
|
enum class pointer_mode
|
|
{
|
|
unchecked, ///< as const basic_json::operator[]: a discarded view where basic_json's behavior is undefined
|
|
checked, ///< as basic_json::at(): out_of_range.401/403
|
|
value, ///< as basic_json::value(): no out_of_range exceptions (the default value is used)
|
|
contains, ///< as basic_json::contains(): no exceptions at all
|
|
};
|
|
|
|
/// the outcome of reading an array index: detail::parse_array_index(), shared with json_pointer
|
|
using index_status = detail::array_index_status;
|
|
|
|
/// reads an array index like json_pointer::array_index (RFC 6901, Sect. 4),
|
|
/// but reports errors instead of throwing them
|
|
template<typename StringType>
|
|
index_status array_index(const StringType& s, std::size_t& idx) noexcept
|
|
{
|
|
return detail::parse_array_index(s, idx);
|
|
}
|
|
|
|
/// throws the exception json_pointer::array_index throws for this status
|
|
template<typename StringType>
|
|
/// (without a context, as json_pointer::array_index)
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_error(index_status status, const StringType& s)
|
|
{
|
|
switch (status)
|
|
{
|
|
case index_status::leading_zero:
|
|
throw_parse_error(exception_id::pointer_index_leading_zero, concat("array index '", s, "' must not begin with '0'"));
|
|
case index_status::not_a_number:
|
|
throw_parse_error(exception_id::pointer_index_not_a_number, concat("array index '", s, "' is not a number"));
|
|
case index_status::exceeds_size_type:
|
|
throw_out_of_range(exception_id::value_out_of_range, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
|
case index_status::unresolved:
|
|
case index_status::ok:
|
|
default:
|
|
throw_out_of_range(exception_id::pointer_unresolved, concat("unresolved reference token '", s, "'"));
|
|
}
|
|
}
|
|
|
|
/*!
|
|
@brief resolve the reference tokens of a JSON pointer, starting at a view
|
|
|
|
The exceptions are those basic_json throws for the same pointer; where
|
|
basic_json's behavior is undefined (a missing key or an index out of range
|
|
with const operator[]), the result is a discarded view.
|
|
*/
|
|
template<typename View, typename Tokens>
|
|
View resolve_pointer(View cur, const Tokens& tokens, pointer_mode mode)
|
|
{
|
|
using string_view_t = typename View::string_view_t;
|
|
const bool throwing = mode == pointer_mode::unchecked || mode == pointer_mode::checked;
|
|
for (const auto& token : tokens)
|
|
{
|
|
if (cur.is_object())
|
|
{
|
|
const auto it = cur.find(string_view_t(token.data(), token.size()));
|
|
if (it == cur.end())
|
|
{
|
|
if (mode == pointer_mode::checked)
|
|
{
|
|
throw_key_not_found(site_of(cur), token);
|
|
}
|
|
return View();
|
|
}
|
|
cur = *it;
|
|
}
|
|
else if (cur.is_array())
|
|
{
|
|
if (token.size() == 1 && token[0] == '-')
|
|
{
|
|
if (throwing)
|
|
{
|
|
throw_array_index_past_end(site_of(cur), cur.size());
|
|
}
|
|
return View();
|
|
}
|
|
std::size_t idx = 0;
|
|
const index_status status = array_index(token, idx);
|
|
if (status != index_status::ok)
|
|
{
|
|
const bool parse_error = status == index_status::leading_zero || status == index_status::not_a_number;
|
|
if (throwing || (mode == pointer_mode::value && parse_error))
|
|
{
|
|
throw_array_index_error(status, token);
|
|
}
|
|
return View();
|
|
}
|
|
if (idx >= cur.size())
|
|
{
|
|
if (mode == pointer_mode::checked)
|
|
{
|
|
throw_array_index_out_of_range(site_of(cur), idx);
|
|
}
|
|
return View();
|
|
}
|
|
cur = cur[idx];
|
|
}
|
|
else
|
|
{
|
|
if (throwing)
|
|
{
|
|
throw_out_of_range(site_of(cur), exception_id::pointer_unresolved, concat("unresolved reference token '", token, "'"));
|
|
}
|
|
return View();
|
|
}
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|