mirror of
https://github.com/nlohmann/json.git
synced 2026-10-31 09:55:42 +00:00
- value_path(): check for the empty stack where the last frame is popped, so that the loop is `for (;;)`; update the frame only after the target check; document that the frame reference is not used after pop_back() or push_back() (and add a test with deep documents to run under a sanitizer) - value_range(): flip the container check to remove a level of nesting - diagnostics(): wrap the whole function in the macro check, and compute the path before the bytes, so that nothing is computed when there is no path; the function stays a template - error_site: separate constructors for the two configurations - site_of(): static_assert that it is called with a view - document why the throw helpers without an error_site have none - pointer.hpp: do not split a comment around the template header - docs: "when materialize() sets them"; list the effects of JSON_DIAGNOSTICS and JSON_DIAGNOSTIC_POSITIONS separately Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N5NasBf4ToHzdcoNEPAJhS
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
|
|
/// (without a context, as json_pointer::array_index)
|
|
template<typename StringType>
|
|
[[noreturn]] NLOHMANN_VIEW_NOINLINE void throw_array_index_error(index_status status, const StringType& s)
|
|
{
|
|
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
|