mirror of
https://github.com/nlohmann/json.git
synced 2026-10-03 05:00:30 +00:00
Add element access, lookup, and iteration to json_view
basic_json_view gains the read-only access functions of basic_json: operator[] and at() with keys and indices, front(), back(), find(), contains(), count(), begin()/end(), items() (with structured bindings from C++17 on), and type_name(). They throw the exceptions (ids and messages) that the const functions of basic_json throw; where basic_json has undefined behavior (operator[] with a missing key or an index out of range, front()/back() of an empty container), the view returns a discarded view or throws invalid_iterator.214. Objects are iterated in document order, and all members are visited. With duplicate keys, lookups find the first member, so that a lookup can stop at the first match; parse() keeps the last value. Keys of up to 16 bytes are compared with two overlapping loads instead of memcmp, and most keys are rejected by their length alone, from the index. The iterators and items live in detail/view/iterator.hpp, the lookups in detail/view/lookup.hpp. Tests compare every element and member of 2,000 generated documents with ordered_json, keys of every length around the load sizes, the exceptions against const basic_json, and the iterators. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
#include <iterator> // distance, input_iterator_tag, iterator_traits
|
||||
#include <memory> // unique_ptr
|
||||
#include <string> // string
|
||||
#include <tuple> // tuple_element, tuple_size
|
||||
#include <type_traits> // enable_if, integral_constant, is_base_of, is_integral, is_same, remove_cv, remove_extent
|
||||
#include <utility> // forward, move
|
||||
|
||||
@@ -43,6 +44,8 @@
|
||||
#include <nlohmann/detail/view/document_data.hpp>
|
||||
#include <nlohmann/detail/view/errors.hpp>
|
||||
#include <nlohmann/detail/view/input.hpp>
|
||||
#include <nlohmann/detail/view/iterator.hpp>
|
||||
#include <nlohmann/detail/view/lookup.hpp>
|
||||
#include <nlohmann/detail/view/macro_scope.hpp>
|
||||
#include <nlohmann/detail/view/materialize.hpp>
|
||||
#include <nlohmann/detail/view/node.hpp>
|
||||
@@ -75,6 +78,11 @@ class basic_json_view
|
||||
using size_type = std::size_t;
|
||||
/// std::string_view from C++17 on
|
||||
using string_view_t = detail::view::string_ref;
|
||||
/// forward iterator over elements (arrays) or member values (objects)
|
||||
using iterator = detail::view::view_iterator<basic_json_view>;
|
||||
using const_iterator = iterator;
|
||||
/// a (key, value) item of items()
|
||||
using item = detail::view::view_item<basic_json_view>;
|
||||
|
||||
/// an invalid view (type() == value_t::discarded)
|
||||
basic_json_view() noexcept = default;
|
||||
@@ -162,6 +170,12 @@ class basic_json_view
|
||||
return m_node != nullptr;
|
||||
}
|
||||
|
||||
/// the name of the type, as basic_json::type_name()
|
||||
const char* type_name() const noexcept
|
||||
{
|
||||
return detail::value_type_name(type());
|
||||
}
|
||||
|
||||
//////////////
|
||||
// capacity //
|
||||
//////////////
|
||||
@@ -211,6 +225,219 @@ class basic_json_view
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////
|
||||
// element access //
|
||||
////////////////////
|
||||
|
||||
/// the value of the member with this key (the first one, should the key
|
||||
/// occur more than once); a discarded view if there is none. Throws
|
||||
/// type_error.305 if this is not an object.
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE basic_json_view operator[](string_view_t key) const
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a string argument with ", type_name());
|
||||
}
|
||||
return lookup(key);
|
||||
}
|
||||
|
||||
basic_json_view operator[](const char* key) const
|
||||
{
|
||||
return operator[](string_view_t(key));
|
||||
}
|
||||
|
||||
basic_json_view operator[](const string_t& key) const
|
||||
{
|
||||
return operator[](string_view_t(key.data(), key.size()));
|
||||
}
|
||||
|
||||
/// the element at this index; a discarded view if the index is out of
|
||||
/// range. Throws type_error.305 if this is not an array.
|
||||
basic_json_view operator[](size_type idx) const
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||
{
|
||||
detail::view::throw_type_error(305, "cannot use operator[] with a numeric argument with ", type_name());
|
||||
}
|
||||
return idx < m_node->len ? basic_json_view(m_doc, detail::view::element_at(m_node, idx)) : basic_json_view();
|
||||
}
|
||||
|
||||
/// (an int argument would be ambiguous between size_type and const char*)
|
||||
basic_json_view operator[](int idx) const
|
||||
{
|
||||
return operator[](static_cast<size_type>(idx));
|
||||
}
|
||||
|
||||
/// the value of the member with this key (the first one, should the key
|
||||
/// occur more than once). Throws type_error.304 if this is not an object,
|
||||
/// and out_of_range.403 if there is no such member.
|
||||
basic_json_view at(string_view_t key) const
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_object()))
|
||||
{
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
const basic_json_view r = lookup(key);
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!r))
|
||||
{
|
||||
detail::view::throw_out_of_range(403, detail::concat("key '", std::string(key.data(), key.size()), "' not found"));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
basic_json_view at(const char* key) const
|
||||
{
|
||||
return at(string_view_t(key));
|
||||
}
|
||||
|
||||
basic_json_view at(const string_t& key) const
|
||||
{
|
||||
return at(string_view_t(key.data(), key.size()));
|
||||
}
|
||||
|
||||
/// the element at this index. Throws type_error.304 if this is not an
|
||||
/// array, and out_of_range.401 if the index is out of range.
|
||||
basic_json_view at(size_type idx) const
|
||||
{
|
||||
if (NLOHMANN_VIEW_UNLIKELY(!is_array()))
|
||||
{
|
||||
detail::view::throw_type_error(304, "cannot use at() with ", type_name());
|
||||
}
|
||||
if (NLOHMANN_VIEW_UNLIKELY(idx >= m_node->len))
|
||||
{
|
||||
detail::view::throw_out_of_range(401, detail::concat("array index ", std::to_string(idx), " is out of range"));
|
||||
}
|
||||
return basic_json_view(m_doc, detail::view::element_at(m_node, idx));
|
||||
}
|
||||
|
||||
basic_json_view at(int idx) const
|
||||
{
|
||||
return at(static_cast<size_type>(idx));
|
||||
}
|
||||
|
||||
/// the first element or member value; a primitive value itself. Throws
|
||||
/// invalid_iterator.214 for null, discarded views, and empty containers.
|
||||
basic_json_view front() const
|
||||
{
|
||||
const iterator it = begin();
|
||||
if (NLOHMANN_VIEW_UNLIKELY(it == end()))
|
||||
{
|
||||
detail::view::throw_invalid_iterator(214, "cannot get value");
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
/// the last element or member value (linear in the size); a primitive
|
||||
/// value itself. Throws invalid_iterator.214 for null, discarded views,
|
||||
/// and empty containers.
|
||||
basic_json_view back() const
|
||||
{
|
||||
if (is_structured() && m_node->len != 0)
|
||||
{
|
||||
return basic_json_view(m_doc, detail::view::last_child(m_node) + (is_object() ? 1 : 0));
|
||||
}
|
||||
return front();
|
||||
}
|
||||
|
||||
////////////
|
||||
// lookup //
|
||||
////////////
|
||||
|
||||
/// an iterator to the member with this key (the first one, should the
|
||||
/// key occur more than once), or end(); end() also for non-objects
|
||||
iterator find(string_view_t key) const
|
||||
{
|
||||
if (!is_object())
|
||||
{
|
||||
return end();
|
||||
}
|
||||
const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
|
||||
return k != nullptr ? iterator(m_doc, k, true) : end();
|
||||
}
|
||||
|
||||
iterator find(const char* key) const
|
||||
{
|
||||
return find(string_view_t(key));
|
||||
}
|
||||
|
||||
iterator find(const string_t& key) const
|
||||
{
|
||||
return find(string_view_t(key.data(), key.size()));
|
||||
}
|
||||
|
||||
/// whether this is an object with a member with this key
|
||||
bool contains(string_view_t key) const
|
||||
{
|
||||
return is_object() && detail::view::find_member(*m_doc, m_node, key.data(), key.size()) != nullptr;
|
||||
}
|
||||
|
||||
bool contains(const char* key) const
|
||||
{
|
||||
return contains(string_view_t(key));
|
||||
}
|
||||
|
||||
bool contains(const string_t& key) const
|
||||
{
|
||||
return contains(string_view_t(key.data(), key.size()));
|
||||
}
|
||||
|
||||
/// 1 if this is an object with a member with this key, else 0 (duplicate
|
||||
/// keys count once)
|
||||
size_type count(string_view_t key) const
|
||||
{
|
||||
return contains(key) ? 1 : 0;
|
||||
}
|
||||
|
||||
size_type count(const char* key) const
|
||||
{
|
||||
return count(string_view_t(key));
|
||||
}
|
||||
|
||||
size_type count(const string_t& key) const
|
||||
{
|
||||
return count(string_view_t(key.data(), key.size()));
|
||||
}
|
||||
|
||||
///////////////
|
||||
// iteration //
|
||||
///////////////
|
||||
|
||||
/// the first element or member value, in document order; a primitive
|
||||
/// value is a range of one element (itself), null an empty range
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE iterator begin() const noexcept
|
||||
{
|
||||
if (NLOHMANN_VIEW_LIKELY(is_structured()))
|
||||
{
|
||||
return iterator(m_doc, document_data::first_child(m_node), is_object());
|
||||
}
|
||||
return iterator(m_doc, m_node, false);
|
||||
}
|
||||
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE iterator end() const noexcept
|
||||
{
|
||||
if (NLOHMANN_VIEW_LIKELY(is_structured()))
|
||||
{
|
||||
return iterator(m_doc, document_data::child_end(m_node), is_object());
|
||||
}
|
||||
return iterator(m_doc, (is_null() || is_discarded()) ? m_node : m_node + 1, false);
|
||||
}
|
||||
|
||||
iterator cbegin() const noexcept
|
||||
{
|
||||
return begin();
|
||||
}
|
||||
|
||||
iterator cend() const noexcept
|
||||
{
|
||||
return end();
|
||||
}
|
||||
|
||||
/// (key, value) items; the key of an array element is its index
|
||||
detail::view::view_items<basic_json_view> items() const noexcept
|
||||
{
|
||||
return detail::view::view_items<basic_json_view>(*this);
|
||||
}
|
||||
|
||||
/////////////////
|
||||
// materialize //
|
||||
/////////////////
|
||||
@@ -237,11 +464,20 @@ class basic_json_view
|
||||
|
||||
private:
|
||||
template<typename> friend class basic_json_document;
|
||||
friend iterator;
|
||||
|
||||
basic_json_view(const document_data* d, const node* n) noexcept
|
||||
: m_doc(d), m_node(n)
|
||||
{}
|
||||
|
||||
/// the value of the first member with this key, or a discarded view
|
||||
/// (object required)
|
||||
NLOHMANN_VIEW_ALWAYS_INLINE basic_json_view lookup(string_view_t key) const noexcept
|
||||
{
|
||||
const node* const k = detail::view::find_member(*m_doc, m_node, key.data(), key.size());
|
||||
return k != nullptr ? basic_json_view(m_doc, k + 1) : basic_json_view();
|
||||
}
|
||||
|
||||
const document_data* m_doc = nullptr;
|
||||
const node* m_node = nullptr;
|
||||
};
|
||||
@@ -587,6 +823,31 @@ using ordered_json_view = basic_json_view<ordered_json>;
|
||||
|
||||
NLOHMANN_JSON_NAMESPACE_END
|
||||
|
||||
// tuple protocol for the items of basic_json_view::items() (structured bindings)
|
||||
namespace std // NOLINT(cert-dcl58-cpp)
|
||||
{
|
||||
|
||||
#if defined(__clang__)
|
||||
// Fix: https://github.com/nlohmann/json/issues/1401
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
template<typename View>
|
||||
class tuple_size<::nlohmann::detail::view::view_item<View>> // NOLINT(cert-dcl58-cpp)
|
||||
: public std::integral_constant<std::size_t, 2> {};
|
||||
|
||||
template<std::size_t N, typename View>
|
||||
class tuple_element<N, ::nlohmann::detail::view::view_item<View>> // NOLINT(cert-dcl58-cpp)
|
||||
{
|
||||
public:
|
||||
using type = decltype(std::declval<::nlohmann::detail::view::view_item<View>>().template get<N>());
|
||||
};
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
} // namespace std
|
||||
|
||||
#include <nlohmann/detail/view/macro_unscope.hpp>
|
||||
|
||||
#endif // INCLUDE_NLOHMANN_JSON_VIEW_HPP_
|
||||
|
||||
Reference in New Issue
Block a user