mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 22:47:13 +00:00
Give basic_json_view the read-only access functions of basic_json: operator[] and at() with keys and indices, front()/back(), find(), contains(), count(), begin()/end() and cbegin()/cend(), items() with structured bindings from C++17 on, and type_name(). Exceptions have the ids and messages of the const functions of basic_json. Where basic_json has undefined behavior the view answers safely: operator[] with a missing key or an out-of-range index returns a discarded view, and front()/back() of an empty container throw invalid_iterator.214. Objects are iterated in document order, and all members are visited; duplicate-key lookups find the first member (as yyjson and simdjson do), while parse(), materialize(), and the map conversions keep the last value, as parse() does. Keys of up to 16 bytes are compared with two overlapping loads. Add value conversions: get<T>()/get_to() for arithmetic types, bool, nullptr_t, strings (std::basic_string copied, string_view_t without a copy), BasicJsonType, views, std::vector, and maps with string keys; get_string() for the string without a copy; number_token() for the number exactly as written in the source; value() with keys and JSON pointers; and operator[]/at()/ contains() with JSON pointers. Everything else, including types with from_json(), goes through materialize() of that subtree. get<T>() of arithmetic types is inlined down to the conversion, so reading an integer needs no call. detail::json_pointer_access exposes a pointer's reference tokens to code outside basic_json. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
172 lines
5.6 KiB
C++
172 lines
5.6 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 from a reference token
|
|
enum class index_status
|
|
{
|
|
ok,
|
|
leading_zero, ///< parse_error.106
|
|
not_number, ///< parse_error.109
|
|
unresolved, ///< out_of_range.404
|
|
too_large, ///< out_of_range.410
|
|
};
|
|
|
|
/// 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
|
|
{
|
|
if (s.size() > 1 && s[0] == '0')
|
|
{
|
|
return index_status::leading_zero;
|
|
}
|
|
if (s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))
|
|
{
|
|
return index_status::not_number;
|
|
}
|
|
if (s.empty())
|
|
{
|
|
return index_status::unresolved;
|
|
}
|
|
std::uint64_t v = 0;
|
|
for (std::size_t i = 0; i < s.size(); ++i)
|
|
{
|
|
const auto d = static_cast<unsigned>(static_cast<unsigned char>(s[i])) - '0';
|
|
if (d > 9 || v > ((std::numeric_limits<std::uint64_t>::max)() - d) / 10)
|
|
{
|
|
return index_status::unresolved; // not a number, or beyond unsigned long long
|
|
}
|
|
v = (v * 10) + d;
|
|
}
|
|
if (v >= (std::numeric_limits<std::size_t>::max)()) // (std::size_t converts to std::uint64_t implicitly)
|
|
{
|
|
return index_status::too_large;
|
|
}
|
|
idx = static_cast<std::size_t>(v);
|
|
return index_status::ok;
|
|
}
|
|
|
|
/// throws the exception json_pointer::array_index throws for this status
|
|
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(106, concat("array index '", s, "' must not begin with '0'"));
|
|
case index_status::not_number:
|
|
throw_parse_error(109, concat("array index '", s, "' is not a number"));
|
|
case index_status::too_large:
|
|
throw_out_of_range(410, concat("array index ", s, " exceeds size_type")); // LCOV_EXCL_LINE
|
|
case index_status::unresolved:
|
|
case index_status::ok:
|
|
default:
|
|
throw_out_of_range(404, 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_out_of_range(403, concat("key '", token, "' not found"));
|
|
}
|
|
return View();
|
|
}
|
|
cur = *it;
|
|
}
|
|
else if (cur.is_array())
|
|
{
|
|
if (token.size() == 1 && token[0] == '-')
|
|
{
|
|
if (throwing)
|
|
{
|
|
throw_out_of_range(402, concat("array index '-' (", std::to_string(cur.size()), ") is out of range"));
|
|
}
|
|
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_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_out_of_range(401, concat("array index ", std::to_string(idx), " is out of range"));
|
|
}
|
|
return View();
|
|
}
|
|
cur = cur[idx];
|
|
}
|
|
else
|
|
{
|
|
if (throwing)
|
|
{
|
|
throw_out_of_range(404, concat("unresolved reference token '", token, "'"));
|
|
}
|
|
return View();
|
|
}
|
|
}
|
|
return cur;
|
|
}
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|