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>
287 lines
7.3 KiB
C++
287 lines
7.3 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> // ptrdiff_t, size_t
|
|
#include <iterator> // forward_iterator_tag
|
|
#include <string> // string, to_string
|
|
#include <type_traits> // enable_if
|
|
|
|
#include <nlohmann/json.hpp>
|
|
#include <nlohmann/detail/view/document_data.hpp>
|
|
#include <nlohmann/detail/view/errors.hpp>
|
|
#include <nlohmann/detail/view/macro_scope.hpp>
|
|
#include <nlohmann/detail/view/node.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
namespace view
|
|
{
|
|
|
|
/// the result of view_iterator::operator->: keeps the view alive for the
|
|
/// duration of the member access
|
|
template<typename View>
|
|
class arrow_proxy
|
|
{
|
|
public:
|
|
explicit arrow_proxy(const View& v) noexcept
|
|
: m_view(v)
|
|
{}
|
|
|
|
const View* operator->() const noexcept
|
|
{
|
|
return &m_view;
|
|
}
|
|
|
|
private:
|
|
View m_view;
|
|
};
|
|
|
|
/*!
|
|
@brief forward iterator over the elements of a basic_json_view
|
|
|
|
Iterates over the elements of an array or the member values of an object, in
|
|
document order; key() gives the key of an object member. As for basic_json, a
|
|
primitive value iterates as a range of one element (itself), and null as an
|
|
empty range.
|
|
*/
|
|
template<typename View>
|
|
class view_iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::forward_iterator_tag;
|
|
using value_type = View;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = arrow_proxy<View>;
|
|
using reference = View;
|
|
using string_view_t = typename View::string_view_t;
|
|
|
|
view_iterator() noexcept = default;
|
|
|
|
/// @param[in] pos the element, or the key of the member
|
|
/// @param[in] object whether pos is a key (its value is the next node)
|
|
/// @param[in] container the value iterated over (the context of the
|
|
/// exceptions, kept only with JSON_DIAGNOSTICS or
|
|
/// JSON_DIAGNOSTIC_POSITIONS)
|
|
view_iterator(const document_data* d, const node* pos, bool object, const node* container) noexcept
|
|
: m_doc(d), m_pos(pos), m_value_offset(object ? 1 : 0)
|
|
#if JSON_DIAGNOSTICS || JSON_DIAGNOSTIC_POSITIONS
|
|
, m_container(container)
|
|
#endif
|
|
{
|
|
static_cast<void>(container);
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE View operator*() const noexcept
|
|
{
|
|
return View(m_doc, View::navigation::value(m_pos + m_value_offset));
|
|
}
|
|
|
|
pointer operator->() const noexcept
|
|
{
|
|
return pointer(**this);
|
|
}
|
|
|
|
NLOHMANN_VIEW_ALWAYS_INLINE view_iterator& operator++() noexcept
|
|
{
|
|
m_pos = document_data::after(m_pos + m_value_offset);
|
|
return *this;
|
|
}
|
|
|
|
view_iterator operator++(int) noexcept
|
|
{
|
|
const view_iterator r = *this;
|
|
++*this;
|
|
return r;
|
|
}
|
|
|
|
friend bool operator==(const view_iterator& a, const view_iterator& b) noexcept
|
|
{
|
|
return a.m_pos == b.m_pos;
|
|
}
|
|
|
|
friend bool operator!=(const view_iterator& a, const view_iterator& b) noexcept
|
|
{
|
|
return a.m_pos != b.m_pos;
|
|
}
|
|
|
|
/// the key of the current object member; throws invalid_iterator.207 for
|
|
/// other iterators, like basic_json's iterators
|
|
string_view_t key() const
|
|
{
|
|
if (NLOHMANN_VIEW_UNLIKELY(m_value_offset == 0))
|
|
{
|
|
throw_invalid_iterator(site(), exception_id::iterator_key_not_object, "cannot use key() for non-object iterators");
|
|
}
|
|
return string_view_t(m_doc->str(*m_pos), m_pos->len);
|
|
}
|
|
|
|
View value() const noexcept
|
|
{
|
|
return **this;
|
|
}
|
|
|
|
/// whether the iterator runs over the members of an object
|
|
bool is_object_iterator() const noexcept
|
|
{
|
|
return m_value_offset != 0;
|
|
}
|
|
|
|
private:
|
|
/// the value iterated over, as the context of an exception
|
|
error_site site() const noexcept
|
|
{
|
|
#if JSON_DIAGNOSTICS || JSON_DIAGNOSTIC_POSITIONS
|
|
return error_site(m_doc, m_container);
|
|
#else
|
|
return error_site();
|
|
#endif
|
|
}
|
|
|
|
const document_data* m_doc = nullptr;
|
|
const node* m_pos = nullptr;
|
|
std::size_t m_value_offset = 0; ///< 1 for objects: the value follows its key
|
|
#if JSON_DIAGNOSTICS || JSON_DIAGNOSTIC_POSITIONS
|
|
const node* m_container = nullptr;
|
|
#endif
|
|
};
|
|
|
|
/*!
|
|
@brief a (key, value) item of basic_json_view::items()
|
|
|
|
The key of an array element is its index, as for basic_json::items().
|
|
Supports structured bindings: for (const auto [key, value] : view.items())
|
|
*/
|
|
template<typename View>
|
|
class view_item
|
|
{
|
|
public:
|
|
using string_view_t = typename View::string_view_t;
|
|
using iterator = view_iterator<View>;
|
|
|
|
view_item(const iterator& it, std::size_t index)
|
|
: m_it(it)
|
|
, m_position(index)
|
|
{}
|
|
|
|
/// the member key, or the element index for arrays (as text made on the
|
|
/// first call, so that iterating over the values pays nothing for it)
|
|
string_view_t key() const
|
|
{
|
|
if (m_it.is_object_iterator())
|
|
{
|
|
return m_it.key();
|
|
}
|
|
if (m_index.empty())
|
|
{
|
|
m_index = std::to_string(m_position);
|
|
}
|
|
return string_view_t(m_index.data(), m_index.size());
|
|
}
|
|
|
|
View value() const noexcept
|
|
{
|
|
return *m_it;
|
|
}
|
|
|
|
template<std::size_t N, typename std::enable_if<N == 0, int>::type = 0>
|
|
string_view_t get() const
|
|
{
|
|
return key();
|
|
}
|
|
|
|
template<std::size_t N, typename std::enable_if<N == 1, int>::type = 0>
|
|
View get() const noexcept
|
|
{
|
|
return value();
|
|
}
|
|
|
|
private:
|
|
iterator m_it;
|
|
std::size_t m_position; ///< the index of an array element
|
|
mutable std::string m_index{}; ///< the text of m_position, once key() was called // NOLINT(readability-redundant-member-init)
|
|
};
|
|
|
|
/// the range returned by basic_json_view::items()
|
|
template<typename View>
|
|
class view_items
|
|
{
|
|
public:
|
|
using item = view_item<View>;
|
|
|
|
class iterator
|
|
{
|
|
public:
|
|
using iterator_category = std::forward_iterator_tag;
|
|
using value_type = item;
|
|
using difference_type = std::ptrdiff_t;
|
|
using pointer = void;
|
|
using reference = item;
|
|
|
|
explicit iterator(const view_iterator<View>& it) noexcept
|
|
: m_it(it)
|
|
{}
|
|
|
|
item operator*() const
|
|
{
|
|
return item(m_it, m_index);
|
|
}
|
|
|
|
iterator& operator++() noexcept
|
|
{
|
|
++m_it;
|
|
++m_index;
|
|
return *this;
|
|
}
|
|
|
|
iterator operator++(int) noexcept
|
|
{
|
|
const iterator r = *this;
|
|
++*this;
|
|
return r;
|
|
}
|
|
|
|
friend bool operator==(const iterator& a, const iterator& b) noexcept
|
|
{
|
|
return a.m_it == b.m_it;
|
|
}
|
|
|
|
friend bool operator!=(const iterator& a, const iterator& b) noexcept
|
|
{
|
|
return a.m_it != b.m_it;
|
|
}
|
|
|
|
private:
|
|
view_iterator<View> m_it;
|
|
std::size_t m_index = 0;
|
|
};
|
|
|
|
explicit view_items(const View& v) noexcept
|
|
: m_view(v)
|
|
{}
|
|
|
|
iterator begin() const noexcept
|
|
{
|
|
return iterator(m_view.begin());
|
|
}
|
|
|
|
iterator end() const noexcept
|
|
{
|
|
return iterator(m_view.end());
|
|
}
|
|
|
|
private:
|
|
View m_view;
|
|
};
|
|
|
|
} // namespace view
|
|
} // namespace detail
|
|
NLOHMANN_JSON_NAMESPACE_END
|