json_view: the library's std conversions read views directly

The library's from_json() overloads (containers, maps, std::array, C
arrays, pairs, tuples, optional, valarray, forward_list, enums, binary,
paths, and scalars) accept views: their traits use the view's
basic_json type, and the few accesses that differ (get_ptr) go through
small helpers. from_json() of a view now finds them; only the probe
still looks up by ADL only. get<T>() of a view thus converts these types
without materialize(), with the same values and exceptions; from an
object, a repeated key keeps the last value, as parse() does.

The view keeps its direct paths for vectors of the types it converts
directly (faster than from_json()) and for containers of views; its
other vector and map paths are replaced by from_json(). For basic_json,
std::valarray and std::forward_list are read in one pass (40 % and 3.5 %
fewer instructions); all other conversions are unchanged.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann committed 2026-10-26 07:12:25 +01:00
1 parent 6212183c5e
commit 46b6daec9f
10 files changed
+598 -220

No files matched your search

+18 -15
View File
@@ -19,16 +19,19 @@ For the types below, the conversion works directly on the flat index -- no `Basi
document's own buffer of decoded strings (see [`get_string()`](get_string.md))
- `BasicJsonType` -- equivalent to [`materialize()`](materialize.md)
- `basic_json_view` -- returns `#!cpp *this`
- `#!cpp std::vector<U, A>` -- from an array, element by element, each converted with `#!cpp get<U>()`; `#!cpp
std::vector<basic_json_view>` keeps a view of every element instead of a value
- `#!cpp std::map<K, V, C, A>` and `#!cpp std::unordered_map<K, V, H, E, A>`, if `K` is constructible from a `#!cpp
(const char*, std::size_t)` pair -- from an object, member by member, each value converted with `#!cpp get<V>()`; with a repeated
key, the *last* value is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) (and
[`materialize()`](materialize.md)) does; `#!cpp std::map<std::string, basic_json_view>` keeps views of the members
instead of values
- `#!cpp std::vector<U, A>` of the types above -- from an array, element by element, each converted with
`#!cpp get<U>()`; `#!cpp std::vector<basic_json_view>` keeps a view of every element instead of a value
- `#!cpp std::map<K, basic_json_view, C, A>` and `#!cpp std::unordered_map<K, basic_json_view, H, E, A>`, if `K` is
constructible from a `#!cpp (const char*, std::size_t)` pair -- views of the members of an object; with a repeated
key, the view of the *last* member is kept, as [`BasicJsonType::parse()`](../basic_json/parse.md) keeps its value
A default-constructible `T` whose `from_json()` accepts views reads the view directly as well, member by member, without
building a `BasicJsonType` value. This covers the types of the
A default-constructible `T` whose `from_json()` accepts views reads the view directly as well, without building a
`BasicJsonType` value. This covers the library's own conversions of the standard library types -- all other
`#!cpp std::vector`s, `#!cpp std::map`/`#!cpp std::unordered_map` (also with other key types), `#!cpp std::list`,
`#!cpp std::deque`, `#!cpp std::set`, `#!cpp std::forward_list`, `#!cpp std::valarray`, `#!cpp std::array`, C arrays,
`#!cpp std::pair`, `#!cpp std::tuple`, `#!cpp std::optional`, enumerations, `#!cpp std::filesystem::path`, ... --
which give the same values as `#!cpp materialize().get<T>()` (from an object, a repeated key keeps the *last* value,
as [`BasicJsonType::parse()`](../basic_json/parse.md) does), as well as the types of the
[`NLOHMANN_DEFINE_TYPE_*`](../macros/nlohmann_define_type_intrusive.md) and
[`NLOHMANN_DEFINE_DERIVED_TYPE_*`](../macros/nlohmann_define_derived_type.md) macros and the enumerations of
[`NLOHMANN_JSON_SERIALIZE_ENUM`](../macros/nlohmann_json_serialize_enum.md), and any `from_json()` constrained to
@@ -44,8 +47,8 @@ T&)` of [arbitrary type conversions](../../features/arbitrary_types.md), is not
keeps it: for `#!cpp {"x":1,"x":2}`, `#!cpp get<point>()` reads `#!cpp x == 1`. See
[Duplicate keys](../../features/json_view.md#duplicate-keys).
Every other `T` -- `#!cpp std::list`, `#!cpp std::pair`, `#!cpp std::array`, user types whose `from_json()` is
written for `BasicJsonType` only, ... -- is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
Every other `T` -- user types whose `from_json()` is written for `BasicJsonType` only, types without a default
constructor, `#!cpp std::tuple`s holding references, ... -- is converted by `#!cpp materialize().get<T>()`: the subtree is built into a real `BasicJsonType`
value first (as [`BasicJsonType::parse()`](../basic_json/parse.md) would), and converted from there exactly as
[`BasicJsonType::get<T>()`](../basic_json/get.md) would convert it.
@@ -70,10 +73,10 @@ Strong guarantee: there are no changes to the view or the document it refers to.
throws for the same JSON type and `T`.
- For `#!cpp std::vector<U, A>`: throws `type_error.302` if the value is not an array; otherwise, whatever converting
an element to `U` throws.
- For `#!cpp std::map`/`#!cpp std::unordered_map`: throws `type_error.302` if the value is not an object; otherwise,
whatever converting a member to the mapped type throws.
- For a `T` whose `from_json()` reads the view: whatever that `from_json()` throws; for the macros, the same
exceptions with the same messages as [`BasicJsonType::get<T>()`](../basic_json/get.md) of the same value.
- For `#!cpp std::map`/`#!cpp std::unordered_map` of views: throws `type_error.302` if the value is not an object.
- For a `T` whose `from_json()` reads the view: whatever that `from_json()` throws; for the library's conversions and
the macros, the same exceptions with the same messages as [`BasicJsonType::get<T>()`](../basic_json/get.md) of the
same value.
- For every other `T`: whatever [`materialize().get<T>()`](../basic_json/get.md) throws -- typically `type_error.302`,
or whatever a user-provided `from_json()` throws.
@@ -9,8 +9,7 @@ Converts the value to `T` and stores it in `v`, in one of three ways:
- For the types that [`get<T>()`](get.md) converts directly -- arithmetic types, `#!cpp bool`,
`#!cpp std::nullptr_t`, [`string_view_t`](index.md#member-types), `#!cpp std::basic_string`, `BasicJsonType`,
`basic_json_view`, `#!cpp std::vector`, and `#!cpp std::map`/`#!cpp std::unordered_map` with string keys -- `v` is
assigned: `#!cpp v = get<T>()`.
`basic_json_view`, vectors of these, and maps of views -- `v` is assigned: `#!cpp v = get<T>()`.
- For a type whose `from_json()` accepts views (see [`get<T>()`](get.md)), that `from_json()` is called with the
view and the existing `v`, without building a `BasicJsonType` value.
- For every other type, the subtree is built into a `BasicJsonType` value and `#!cpp materialize().get_to(v)` is
+197 -74
View File
@@ -45,6 +45,65 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
// The from_json() overloads below read basic_json values and the views of
// <nlohmann/json_view.hpp> (BasicJsonType is then a view, and
// json_type_of_t<BasicJsonType> its basic_json type). The helpers below are
// the accesses that differ between the two.
// the boolean of j, a boolean
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
typename BasicJsonType::boolean_t boolean_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
bool boolean_value(const View& v)
{
return v.template get<bool>();
}
// the string of j, a string
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::string_t& string_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::string_t string_value(const View& v)
{
const auto s = v.get_string();
return typename View::string_t(s.data(), s.size());
}
// the binary value of j, a binary value (never a view: a view of a JSON text
// holds no binary values)
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::binary_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::basic_json_type::binary_t binary_value(const View& /*unused*/)
{
return {}; // LCOV_EXCL_LINE
}
// the elements of j, an array, for a range-based for loop
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::array_t& array_elements(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
View array_elements(const View& v) noexcept
{
return v; // (a view is two pointers)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
@@ -73,7 +132,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
// read a number value of any of the three number types into val;
// returns false (leaving val untouched) if j is not a number
template<typename BasicJsonType, typename ArithmeticType>
template < typename BasicJsonType, typename ArithmeticType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
@@ -110,10 +169,22 @@ bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
}
}
// a view converts numbers itself (as basic_json does)
template<typename View, typename ArithmeticType, enable_if_t<is_json_view<View>::value, int> = 0>
bool get_number_value(const View& v, ArithmeticType& val)
{
if (!v.is_number())
{
return false;
}
val = v.template get<ArithmeticType>();
return true;
}
// overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
int > = 0 >
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
@@ -124,31 +195,31 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
throw_type_must_be("boolean", j);
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
b = boolean_value(j);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
throw_type_must_be("string", j);
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
s = string_value(j);
}
template <
typename BasicJsonType, typename StringType,
enable_if_t <
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
&& is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
std::is_assignable<StringType&, const typename json_type_of_t<BasicJsonType>::string_t>::value
&& is_detected_exact<typename json_type_of_t<BasicJsonType>::string_t::value_type, value_type_t, StringType>::value
&& !std::is_same<typename json_type_of_t<BasicJsonType>::string_t, StringType>::value
&& !is_json_ref<StringType>::value, int > = 0 >
inline void from_json(const BasicJsonType& j, StringType& s)
{
@@ -157,23 +228,23 @@ inline void from_json(const BasicJsonType& j, StringType& s)
throw_type_must_be("string", j);
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
s = string_value(j);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<json_type_of_t<BasicJsonType>>& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
@@ -185,8 +256,8 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
{
using underlying_type = typename std::underlying_type<EnumType>::type;
// get_arithmetic_value() does not accept boolean_t; read the number that to_json() wrote instead
using value_type = typename std::conditional<std::is_same<underlying_type, typename BasicJsonType::boolean_t>::value,
typename BasicJsonType::number_unsigned_t, underlying_type>::type;
using value_type = typename std::conditional<std::is_same<underlying_type, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
typename json_type_of_t<BasicJsonType>::number_unsigned_t, underlying_type>::type;
value_type val;
get_arithmetic_value(j, val);
e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val));
@@ -195,7 +266,7 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -203,16 +274,16 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
throw_type_must_be("array", j);
}
l.clear();
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
auto pos = l.before_begin();
for (const auto& elem : array_elements(j))
{
return i.template get<T>();
});
pos = l.insert_after(pos, elem.template get<T>());
}
}
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -220,11 +291,11 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
throw_type_must_be("array", j);
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
std::size_t i = 0;
for (const auto& elem : array_elements(j))
{
return elem.template get<T>();
});
l[i++] = elem.template get<T>();
}
}
// element is not itself a C array: read it directly
@@ -252,7 +323,8 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
from_json_c_array_element(j, arr);
}
template<typename BasicJsonType>
// (for a view, array_t is read element by element below)
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -261,7 +333,7 @@ inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType:
// fill a std::array from the JSON array j: the size is checked once and the
// elements are read in order, so a short array converts the elements it has and
// then throws the out_of_range error at() would throw for the first missing one
template<typename BasicJsonType, typename T, std::size_t N>
template < typename BasicJsonType, typename T, std::size_t N, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
{
const auto& inner_array = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -276,6 +348,21 @@ void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
}
}
// (a view has no random access to the elements: they are read in order)
template<typename View, typename T, std::size_t N, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_std_array(const View& v, std::array<T, N>& arr)
{
std::size_t size = 0;
for (auto it = v.begin(); size < N && it != v.end(); ++it)
{
arr[size++] = (*it).template get<T>();
}
if (JSON_HEDLEY_UNLIKELY(size < N))
{
static_cast<void>(v.at(size));
}
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
priority_tag<2> /*unused*/)
@@ -324,9 +411,8 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
void())
{
ConstructibleArrayType ret;
const auto* inner_array = j.template get_ptr<const typename BasicJsonType::array_t*>();
reserve_if_possible(ret, inner_array->size(), priority_tag<1> {});
for (const auto& elem : *inner_array)
reserve_if_possible(ret, j.size(), priority_tag<1> {});
for (const auto& elem : array_elements(j))
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
@@ -337,11 +423,11 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
template < typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t <
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
is_constructible_array_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_constructible_string_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename json_type_of_t<BasicJsonType>::binary_t>::value&&
!is_compatible_binary_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_basic_json<ConstructibleArrayType>::value,
int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
@@ -408,24 +494,24 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
throw_type_must_be("binary", j);
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
bin = binary_value(j);
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
enable_if_t < is_compatible_binary_type<json_type_of_t<BasicJsonType>, CompatibleArrayType>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
{
if (j.is_binary())
{
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
bin = static_cast<CompatibleArrayType>(binary_value(j));
}
else if (j.is_array())
{
@@ -454,8 +540,47 @@ void from_json_object_emplace(ConstructibleObjectType& obj, Key&& key, Value&& v
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
}
// a member of a view's object: a later member with the same key replaces an
// earlier one, as parse() keeps the last value of a repeated key (containers
// without operator[] keep the first)
template<typename ConstructibleObjectType, typename Key, typename Value>
auto from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<1> /*unused*/)
-> decltype(obj[std::forward<Key>(key)] = std::forward<Value>(value), void())
{
obj[std::forward<Key>(key)] = std::forward<Value>(value);
}
template<typename ConstructibleObjectType, typename Key, typename Value>
void from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<0> /*unused*/)
{
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
}
template < typename BasicJsonType, typename ConstructibleObjectType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& ret)
{
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename View, typename ConstructibleObjectType, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_object_impl(const View& v, ConstructibleObjectType& ret)
{
using string_t = typename View::basic_json_type::object_t::key_type;
reserve_if_possible(ret, v.size(), priority_tag<1> {});
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(ret, string_t(key.data(), key.size()), item.value().template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
enable_if_t<is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleObjectType>::value, int> = 0>
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
@@ -464,12 +589,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
}
ConstructibleObjectType ret;
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
from_json_object_impl(j, ret);
obj = std::move(ret);
}
@@ -478,10 +598,10 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t <
std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_float_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
@@ -495,7 +615,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
throw_type_must_be("number", j);
}
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
val = static_cast<ArithmeticType>(boolean_value(j));
}
template<typename BasicJsonType, typename Type>
@@ -505,7 +625,7 @@ detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::iden
}
template<typename BasicJsonType, typename Type,
detail::enable_if_t<detail::is_compatible_reference_type<BasicJsonType, Type>::value, int> = 0>
detail::enable_if_t<conjunction<is_basic_json<uncvref_t<BasicJsonType>>, is_compatible_reference_type<BasicJsonType, Type>>::value, int> = 0>
Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/)
{
return std::forward<BasicJsonType>(j).template get_ref<Type>();
@@ -549,7 +669,7 @@ inline void from_json_tuple_impl(const BasicJsonType& j, std::pair<A1, A2>& p, p
template<typename BasicJsonType, typename... Args>
std::tuple<Args...> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, is_compatible_reference_type<const BasicJsonType&, Args>>...>::value,
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, conjunction<is_basic_json<BasicJsonType>, is_compatible_reference_type<const BasicJsonType&, Args>>>...>::value,
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
return from_json_tuple_impl_base<1, Args...>(j, index_sequence_for<Args...> {});
}
@@ -595,7 +715,7 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
// read a map with enum keys from an object, using the enum's own from_json for
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename BasicJsonType, typename Map>
template < typename BasicJsonType, typename Map, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
{
if (!j.is_object())
@@ -610,6 +730,24 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::tru
return true;
}
template<typename View, typename Map, enable_if_t<is_json_view<View>::value, int> = 0>
inline bool from_json_enum_keyed_object(const View& v, Map& m, std::true_type /*key is enum*/)
{
using json_t = typename View::basic_json_type;
if (!v.is_object())
{
return false;
}
m.clear();
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(m, json_t(typename json_t::string_t(key.data(), key.size())).template get<typename Map::key_type>(),
item.value().template get<typename Map::mapped_type>(), priority_tag<1> {});
}
return true;
}
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
{
@@ -618,7 +756,7 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/,
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -631,7 +769,7 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -655,7 +793,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{
throw_type_must_be("string", j);
}
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
const auto& s = string_value(j);
// Checking for C++20 standard or later can be insufficient in case the
// library support for char8_t is either incomplete or was disabled
// altogether. Use the __cpp_lib_char8_t feature test instead.
@@ -694,10 +832,9 @@ struct fn
};
} // namespace from_json_adl
// the sources whose from_json() is only looked up by ADL: views (the library's
// overloads are written for basic_json) and the probe
// the sources whose from_json() is only looked up by ADL: the probe
template<typename T>
struct is_adl_only_from_json_source : disjunction<is_json_view<T>, std::is_same<T, from_json_adl::probe>> {};
struct is_adl_only_from_json_source : std::is_same<T, from_json_adl::probe> {};
struct from_json_fn
{
@@ -748,20 +885,6 @@ void from_json_base(const View& v, T& base)
v.get_to(base);
}
// the context of an exception thrown by a from_json() of the macros: the value
// itself for basic_json (for JSON_DIAGNOSTICS), none for a view
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
const BasicJsonType * from_json_context(const BasicJsonType& j) noexcept
{
return &j;
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
std::nullptr_t from_json_context(const View& /*unused*/) noexcept
{
return nullptr;
}
// a value as an exception message shows it (invalid UTF-8 replaced; the
// strings of a view are always valid UTF-8)
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
+15 -1
View File
@@ -417,6 +417,20 @@ void templated_json_throw(ExceptionType exception)
(void)exception;
}
// the context of an exception about j: j itself for a basic_json value (whose
// path JSON_DIAGNOSTICS shows), none for a view of <nlohmann/json_view.hpp>
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
const BasicJsonType * exception_context(const BasicJsonType& j) noexcept
{
return &j;
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
std::nullptr_t exception_context(const View& /*unused*/) noexcept
{
return nullptr;
}
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@@ -428,7 +442,7 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const cha
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), exception_context(j)));
}
/*!
+1 -1
View File
@@ -347,7 +347,7 @@
return ej_pair.second == j; \
}); \
if (it != std::end(m)) e = it->first; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::from_json_context(j))); \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::exception_context(j))); \
}
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
+7 -5
View File
@@ -130,14 +130,16 @@ struct is_direct_value : std::integral_constant < bool,
template<typename Traits, typename Alloc, typename BasicJsonType, typename View>
struct is_direct_value<std::basic_string<char, Traits, Alloc>, BasicJsonType, View> : std::true_type {};
// (vectors of these, which are faster than through from_json(), and containers
// of views; other containers are read by the library's from_json())
template<typename U, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::vector<U, A>, BasicJsonType, View> : std::true_type {};
struct is_direct_value<std::vector<U, A>, BasicJsonType, View> : is_direct_value<U, BasicJsonType, View> {};
template<typename K, typename V, typename C, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::map<K, V, C, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename C, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::map<K, View, C, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename V, typename H, typename E, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::unordered_map<K, V, H, E, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename H, typename E, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::unordered_map<K, View, H, E, A>, BasicJsonType, View> : is_string_key<K> {};
} // namespace view
} // namespace detail
+10 -7
View File
@@ -910,22 +910,25 @@ class basic_json_view
return *this;
}
template<typename U, typename A>
/// vectors of the types converted directly (faster than the library's
/// from_json()) and containers of views (no from_json() reads them: a view
/// is no value of BasicJsonType); other containers are read by from_json()
template < typename U, typename A, typename std::enable_if < detail::view::is_direct_value<U, BasicJsonType, basic_json_view>::value, int >::type = 0 >
std::vector<U, A> get_impl(detail::view::value_tag<std::vector<U, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::vector_value<basic_json_view, U, A>(*this);
}
template<typename K, typename V, typename C, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::map<K, V, C, A> get_impl(detail::view::value_tag<std::map<K, V, C, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
template<typename K, typename C, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::map<K, basic_json_view, C, A> get_impl(detail::view::value_tag<std::map<K, basic_json_view, C, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::map_value<std::map<K, V, C, A>>(*this);
return detail::view::map_value<std::map<K, basic_json_view, C, A>>(*this);
}
template<typename K, typename V, typename H, typename E, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::unordered_map<K, V, H, E, A> get_impl(detail::view::value_tag<std::unordered_map<K, V, H, E, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
template<typename K, typename H, typename E, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::unordered_map<K, basic_json_view, H, E, A> get_impl(detail::view::value_tag<std::unordered_map<K, basic_json_view, H, E, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::map_value<std::unordered_map<K, V, H, E, A>>(*this);
return detail::view::map_value<std::unordered_map<K, basic_json_view, H, E, A>>(*this);
}
/// whether the from_json() of T's serializer reads views (see
+213 -76
View File
@@ -2720,7 +2720,7 @@ JSON_HEDLEY_DIAGNOSTIC_POP
return ej_pair.second == j; \
}); \
if (it != std::end(m)) e = it->first; \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::from_json_context(j))); \
else ::nlohmann::detail::templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(nlohmann::detail::exception_id::value_out_of_range, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", nlohmann::detail::from_json_dump(j)), nlohmann::detail::exception_context(j))); \
}
// Ugly macros to avoid uglier copy-paste when specializing basic_json. They
@@ -5914,6 +5914,20 @@ void templated_json_throw(ExceptionType exception)
(void)exception;
}
// the context of an exception about j: j itself for a basic_json value (whose
// path JSON_DIAGNOSTICS shows), none for a view of <nlohmann/json_view.hpp>
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
const BasicJsonType * exception_context(const BasicJsonType& j) noexcept
{
return &j;
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
std::nullptr_t exception_context(const View& /*unused*/) noexcept
{
return nullptr;
}
/*!
@brief throws because @a j does not have the type a conversion expects
@param[in] expected the expected type(s), e.g. "array" or "binary or array"
@@ -5925,7 +5939,7 @@ JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE void throw_type_must_be(const cha
{
static_cast<void>(expected); // unused when JSON_NOEXCEPTION is defined
static_cast<void>(j);
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), &j));
JSON_THROW(type_error::create(exception_id::type_mismatch, concat("type must be ", expected, ", but is ", j.type_name()), exception_context(j)));
}
/*!
@@ -6154,6 +6168,65 @@ NLOHMANN_JSON_NAMESPACE_BEGIN
namespace detail
{
// The from_json() overloads below read basic_json values and the views of
// <nlohmann/json_view.hpp> (BasicJsonType is then a view, and
// json_type_of_t<BasicJsonType> its basic_json type). The helpers below are
// the accesses that differ between the two.
// the boolean of j, a boolean
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
typename BasicJsonType::boolean_t boolean_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
bool boolean_value(const View& v)
{
return v.template get<bool>();
}
// the string of j, a string
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::string_t& string_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::string_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::string_t string_value(const View& v)
{
const auto s = v.get_string();
return typename View::string_t(s.data(), s.size());
}
// the binary value of j, a binary value (never a view: a view of a JSON text
// holds no binary values)
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::binary_t& binary_value(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::binary_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
typename View::basic_json_type::binary_t binary_value(const View& /*unused*/)
{
return {}; // LCOV_EXCL_LINE
}
// the elements of j, an array, for a range-based for loop
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
const typename BasicJsonType::array_t& array_elements(const BasicJsonType& j)
{
return *j.template get_ptr<const typename BasicJsonType::array_t*>();
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
View array_elements(const View& v) noexcept
{
return v; // (a view is two pointers)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
{
@@ -6182,7 +6255,7 @@ void from_json(const BasicJsonType& j, std::optional<T>& opt)
// read a number value of any of the three number types into val;
// returns false (leaving val untouched) if j is not a number
template<typename BasicJsonType, typename ArithmeticType>
template < typename BasicJsonType, typename ArithmeticType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
{
switch (static_cast<value_t>(j))
@@ -6219,10 +6292,22 @@ bool get_number_value(const BasicJsonType& j, ArithmeticType& val)
}
}
// a view converts numbers itself (as basic_json does)
template<typename View, typename ArithmeticType, enable_if_t<is_json_view<View>::value, int> = 0>
bool get_number_value(const View& v, ArithmeticType& val)
{
if (!v.is_number())
{
return false;
}
val = v.template get<ArithmeticType>();
return true;
}
// overloads for basic_json template parameters
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t < std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
int > = 0 >
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
{
@@ -6233,31 +6318,31 @@ void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::boolean_t& b)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
{
throw_type_must_be("boolean", j);
}
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
b = boolean_value(j);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::string_t& s)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
{
throw_type_must_be("string", j);
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
s = string_value(j);
}
template <
typename BasicJsonType, typename StringType,
enable_if_t <
std::is_assignable<StringType&, const typename BasicJsonType::string_t>::value
&& is_detected_exact<typename BasicJsonType::string_t::value_type, value_type_t, StringType>::value
&& !std::is_same<typename BasicJsonType::string_t, StringType>::value
std::is_assignable<StringType&, const typename json_type_of_t<BasicJsonType>::string_t>::value
&& is_detected_exact<typename json_type_of_t<BasicJsonType>::string_t::value_type, value_type_t, StringType>::value
&& !std::is_same<typename json_type_of_t<BasicJsonType>::string_t, StringType>::value
&& !is_json_ref<StringType>::value, int > = 0 >
inline void from_json(const BasicJsonType& j, StringType& s)
{
@@ -6266,23 +6351,23 @@ inline void from_json(const BasicJsonType& j, StringType& s)
throw_type_must_be("string", j);
}
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
s = string_value(j);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_float_t& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<BasicJsonType>& val)
inline void from_json(const BasicJsonType& j, number_unsigned_overload_t<json_type_of_t<BasicJsonType>>& val)
{
get_arithmetic_value(j, val);
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::number_integer_t& val)
{
get_arithmetic_value(j, val);
}
@@ -6294,8 +6379,8 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
{
using underlying_type = typename std::underlying_type<EnumType>::type;
// get_arithmetic_value() does not accept boolean_t; read the number that to_json() wrote instead
using value_type = typename std::conditional<std::is_same<underlying_type, typename BasicJsonType::boolean_t>::value,
typename BasicJsonType::number_unsigned_t, underlying_type>::type;
using value_type = typename std::conditional<std::is_same<underlying_type, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
typename json_type_of_t<BasicJsonType>::number_unsigned_t, underlying_type>::type;
value_type val;
get_arithmetic_value(j, val);
e = static_cast<EnumType>(bool_aware_static_cast<underlying_type>(val));
@@ -6304,7 +6389,7 @@ inline void from_json(const BasicJsonType& j, EnumType& e)
// forward_list doesn't have an insert method
template<typename BasicJsonType, typename T, typename Allocator,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -6312,16 +6397,16 @@ inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l
throw_type_must_be("array", j);
}
l.clear();
std::transform(j.rbegin(), j.rend(),
std::front_inserter(l), [](const BasicJsonType & i)
auto pos = l.before_begin();
for (const auto& elem : array_elements(j))
{
return i.template get<T>();
});
pos = l.insert_after(pos, elem.template get<T>());
}
}
// valarray doesn't have an insert method
template<typename BasicJsonType, typename T,
enable_if_t<is_getable<BasicJsonType, T>::value, int> = 0>
enable_if_t<is_getable<json_type_of_t<BasicJsonType>, T>::value, int> = 0>
inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
@@ -6329,11 +6414,11 @@ inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
throw_type_must_be("array", j);
}
l.resize(j.size());
std::transform(j.begin(), j.end(), std::begin(l),
[](const BasicJsonType & elem)
std::size_t i = 0;
for (const auto& elem : array_elements(j))
{
return elem.template get<T>();
});
l[i++] = elem.template get<T>();
}
}
// element is not itself a C array: read it directly
@@ -6361,7 +6446,8 @@ auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines
from_json_c_array_element(j, arr);
}
template<typename BasicJsonType>
// (for a view, array_t is read element by element below)
template < typename BasicJsonType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/)
{
arr = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -6370,7 +6456,7 @@ inline void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType:
// fill a std::array from the JSON array j: the size is checked once and the
// elements are read in order, so a short array converts the elements it has and
// then throws the out_of_range error at() would throw for the first missing one
template<typename BasicJsonType, typename T, std::size_t N>
template < typename BasicJsonType, typename T, std::size_t N, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
{
const auto& inner_array = *j.template get_ptr<const typename BasicJsonType::array_t*>();
@@ -6385,6 +6471,21 @@ void from_json_std_array(const BasicJsonType& j, std::array<T, N>& arr)
}
}
// (a view has no random access to the elements: they are read in order)
template<typename View, typename T, std::size_t N, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_std_array(const View& v, std::array<T, N>& arr)
{
std::size_t size = 0;
for (auto it = v.begin(); size < N && it != v.end(); ++it)
{
arr[size++] = (*it).template get<T>();
}
if (JSON_HEDLEY_UNLIKELY(size < N))
{
static_cast<void>(v.at(size));
}
}
template<typename BasicJsonType, typename T, std::size_t N>
auto from_json_array_impl(const BasicJsonType& j, std::array<T, N>& arr,
priority_tag<2> /*unused*/)
@@ -6433,9 +6534,8 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
void())
{
ConstructibleArrayType ret;
const auto* inner_array = j.template get_ptr<const typename BasicJsonType::array_t*>();
reserve_if_possible(ret, inner_array->size(), priority_tag<1> {});
for (const auto& elem : *inner_array)
reserve_if_possible(ret, j.size(), priority_tag<1> {});
for (const auto& elem : array_elements(j))
{
// get<BasicJsonType>() returns *this, this won't call a from_json
// method when value_type is BasicJsonType
@@ -6446,11 +6546,11 @@ auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, p
template < typename BasicJsonType, typename ConstructibleArrayType,
enable_if_t <
is_constructible_array_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_object_type<BasicJsonType, ConstructibleArrayType>::value&&
!is_constructible_string_type<BasicJsonType, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename BasicJsonType::binary_t>::value&&
!is_compatible_binary_type<BasicJsonType, ConstructibleArrayType>::value&&
is_constructible_array_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_constructible_string_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!std::is_same<ConstructibleArrayType, typename json_type_of_t<BasicJsonType>::binary_t>::value&&
!is_compatible_binary_type<json_type_of_t<BasicJsonType>, ConstructibleArrayType>::value&&
!is_basic_json<ConstructibleArrayType>::value,
int > = 0 >
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
@@ -6517,24 +6617,24 @@ auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
}
template<typename BasicJsonType>
inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
inline void from_json(const BasicJsonType& j, typename json_type_of_t<BasicJsonType>::binary_t& bin)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
{
throw_type_must_be("binary", j);
}
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
bin = binary_value(j);
}
template < typename BasicJsonType, typename CompatibleArrayType,
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
enable_if_t < is_compatible_binary_type<json_type_of_t<BasicJsonType>, CompatibleArrayType>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, CompatibleArrayType& bin)
{
if (j.is_binary())
{
bin = static_cast<CompatibleArrayType>(*j.template get_ptr<const typename BasicJsonType::binary_t*>());
bin = static_cast<CompatibleArrayType>(binary_value(j));
}
else if (j.is_array())
{
@@ -6563,8 +6663,47 @@ void from_json_object_emplace(ConstructibleObjectType& obj, Key&& key, Value&& v
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
}
// a member of a view's object: a later member with the same key replaces an
// earlier one, as parse() keeps the last value of a repeated key (containers
// without operator[] keep the first)
template<typename ConstructibleObjectType, typename Key, typename Value>
auto from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<1> /*unused*/)
-> decltype(obj[std::forward<Key>(key)] = std::forward<Value>(value), void())
{
obj[std::forward<Key>(key)] = std::forward<Value>(value);
}
template<typename ConstructibleObjectType, typename Key, typename Value>
void from_json_object_assign(ConstructibleObjectType& obj, Key&& key, Value&& value, priority_tag<0> /*unused*/)
{
obj.emplace(std::forward<Key>(key), std::forward<Value>(value));
}
template < typename BasicJsonType, typename ConstructibleObjectType, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
void from_json_object_impl(const BasicJsonType& j, ConstructibleObjectType& ret)
{
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename View, typename ConstructibleObjectType, enable_if_t<is_json_view<View>::value, int> = 0>
void from_json_object_impl(const View& v, ConstructibleObjectType& ret)
{
using string_t = typename View::basic_json_type::object_t::key_type;
reserve_if_possible(ret, v.size(), priority_tag<1> {});
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(ret, string_t(key.data(), key.size()), item.value().template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
}
template<typename BasicJsonType, typename ConstructibleObjectType,
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
enable_if_t<is_constructible_object_type<json_type_of_t<BasicJsonType>, ConstructibleObjectType>::value, int> = 0>
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
{
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
@@ -6573,12 +6712,7 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
}
ConstructibleObjectType ret;
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
reserve_if_possible(ret, inner_object->size(), priority_tag<1> {});
for (const auto& p : *inner_object)
{
from_json_object_emplace(ret, p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>(), priority_tag<1> {});
}
from_json_object_impl(j, ret);
obj = std::move(ret);
}
@@ -6587,10 +6721,10 @@ inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
template < typename BasicJsonType, typename ArithmeticType,
enable_if_t <
std::is_arithmetic<ArithmeticType>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::number_float_t>::value&&
!std::is_same<ArithmeticType, typename BasicJsonType::boolean_t>::value,
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_unsigned_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_integer_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::number_float_t>::value&&
!std::is_same<ArithmeticType, typename json_type_of_t<BasicJsonType>::boolean_t>::value,
int > = 0 >
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
@@ -6604,7 +6738,7 @@ inline void from_json(const BasicJsonType& j, ArithmeticType& val)
{
throw_type_must_be("number", j);
}
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
val = static_cast<ArithmeticType>(boolean_value(j));
}
template<typename BasicJsonType, typename Type>
@@ -6614,7 +6748,7 @@ detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::iden
}
template<typename BasicJsonType, typename Type,
detail::enable_if_t<detail::is_compatible_reference_type<BasicJsonType, Type>::value, int> = 0>
detail::enable_if_t<conjunction<is_basic_json<uncvref_t<BasicJsonType>>, is_compatible_reference_type<BasicJsonType, Type>>::value, int> = 0>
Type from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<1> /*unused*/)
{
return std::forward<BasicJsonType>(j).template get_ref<Type>();
@@ -6658,7 +6792,7 @@ inline void from_json_tuple_impl(const BasicJsonType& j, std::pair<A1, A2>& p, p
template<typename BasicJsonType, typename... Args>
std::tuple<Args...> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::tuple<Args...>> /*unused*/, priority_tag<2> /*unused*/)
{
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, is_compatible_reference_type<const BasicJsonType&, Args>>...>::value,
static_assert(conjunction<disjunction<negation<std::is_reference<Args>>, conjunction<is_basic_json<BasicJsonType>, is_compatible_reference_type<const BasicJsonType&, Args>>>...>::value,
"Can not return a tuple containing references to types not contained in a Json, try Json::get_to()");
return from_json_tuple_impl_base<1, Args...>(j, index_sequence_for<Args...> {});
}
@@ -6704,7 +6838,7 @@ void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
// read a map with enum keys from an object, using the enum's own from_json for
// the keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); this is the form written
// with JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
template<typename BasicJsonType, typename Map>
template < typename BasicJsonType, typename Map, enable_if_t < !is_json_view<BasicJsonType>::value, int > = 0 >
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
{
if (!j.is_object())
@@ -6719,6 +6853,24 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::tru
return true;
}
template<typename View, typename Map, enable_if_t<is_json_view<View>::value, int> = 0>
inline bool from_json_enum_keyed_object(const View& v, Map& m, std::true_type /*key is enum*/)
{
using json_t = typename View::basic_json_type;
if (!v.is_object())
{
return false;
}
m.clear();
for (const auto item : v.items())
{
const auto key = item.key();
from_json_object_assign(m, json_t(typename json_t::string_t(key.data(), key.size())).template get<typename Map::key_type>(),
item.value().template get<typename Map::mapped_type>(), priority_tag<1> {});
}
return true;
}
template<typename BasicJsonType, typename Map>
inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/, std::false_type /*key is enum*/)
{
@@ -6727,7 +6879,7 @@ inline bool from_json_enum_keyed_object(const BasicJsonType& /*j*/, Map& /*m*/,
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -6740,7 +6892,7 @@ void from_json(const BasicJsonType& j, std::map<Key, Value, Compare, Allocator>&
template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator,
typename = enable_if_t < !std::is_constructible <
typename BasicJsonType::string_t, Key >::value >>
typename json_type_of_t<BasicJsonType>::string_t, Key >::value >>
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
{
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
@@ -6764,7 +6916,7 @@ inline void from_json(const BasicJsonType& j, std_fs::path& p)
{
throw_type_must_be("string", j);
}
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
const auto& s = string_value(j);
// Checking for C++20 standard or later can be insufficient in case the
// library support for char8_t is either incomplete or was disabled
// altogether. Use the __cpp_lib_char8_t feature test instead.
@@ -6803,10 +6955,9 @@ struct fn
};
} // namespace from_json_adl
// the sources whose from_json() is only looked up by ADL: views (the library's
// overloads are written for basic_json) and the probe
// the sources whose from_json() is only looked up by ADL: the probe
template<typename T>
struct is_adl_only_from_json_source : disjunction<is_json_view<T>, std::is_same<T, from_json_adl::probe>> {};
struct is_adl_only_from_json_source : std::is_same<T, from_json_adl::probe> {};
struct from_json_fn
{
@@ -6857,20 +7008,6 @@ void from_json_base(const View& v, T& base)
v.get_to(base);
}
// the context of an exception thrown by a from_json() of the macros: the value
// itself for basic_json (for JSON_DIAGNOSTICS), none for a view
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
const BasicJsonType * from_json_context(const BasicJsonType& j) noexcept
{
return &j;
}
template<typename View, enable_if_t<is_json_view<View>::value, int> = 0>
std::nullptr_t from_json_context(const View& /*unused*/) noexcept
{
return nullptr;
}
// a value as an exception message shows it (invalid UTF-8 replaced; the
// strings of a view are always valid UTF-8)
template<typename BasicJsonType, enable_if_t<is_basic_json<BasicJsonType>::value, int> = 0>
+17 -12
View File
@@ -7797,14 +7797,16 @@ struct is_direct_value : std::integral_constant < bool,
template<typename Traits, typename Alloc, typename BasicJsonType, typename View>
struct is_direct_value<std::basic_string<char, Traits, Alloc>, BasicJsonType, View> : std::true_type {};
// (vectors of these, which are faster than through from_json(), and containers
// of views; other containers are read by the library's from_json())
template<typename U, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::vector<U, A>, BasicJsonType, View> : std::true_type {};
struct is_direct_value<std::vector<U, A>, BasicJsonType, View> : is_direct_value<U, BasicJsonType, View> {};
template<typename K, typename V, typename C, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::map<K, V, C, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename C, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::map<K, View, C, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename V, typename H, typename E, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::unordered_map<K, V, H, E, A>, BasicJsonType, View> : is_string_key<K> {};
template<typename K, typename H, typename E, typename A, typename BasicJsonType, typename View>
struct is_direct_value<std::unordered_map<K, View, H, E, A>, BasicJsonType, View> : is_string_key<K> {};
} // namespace view
} // namespace detail
@@ -8637,22 +8639,25 @@ class basic_json_view
return *this;
}
template<typename U, typename A>
/// vectors of the types converted directly (faster than the library's
/// from_json()) and containers of views (no from_json() reads them: a view
/// is no value of BasicJsonType); other containers are read by from_json()
template < typename U, typename A, typename std::enable_if < detail::view::is_direct_value<U, BasicJsonType, basic_json_view>::value, int >::type = 0 >
std::vector<U, A> get_impl(detail::view::value_tag<std::vector<U, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::vector_value<basic_json_view, U, A>(*this);
}
template<typename K, typename V, typename C, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::map<K, V, C, A> get_impl(detail::view::value_tag<std::map<K, V, C, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
template<typename K, typename C, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::map<K, basic_json_view, C, A> get_impl(detail::view::value_tag<std::map<K, basic_json_view, C, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::map_value<std::map<K, V, C, A>>(*this);
return detail::view::map_value<std::map<K, basic_json_view, C, A>>(*this);
}
template<typename K, typename V, typename H, typename E, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::unordered_map<K, V, H, E, A> get_impl(detail::view::value_tag<std::unordered_map<K, V, H, E, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
template<typename K, typename H, typename E, typename A, typename std::enable_if<detail::view::is_string_key<K>::value, int>::type = 0>
std::unordered_map<K, basic_json_view, H, E, A> get_impl(detail::view::value_tag<std::unordered_map<K, basic_json_view, H, E, A>> /*unused*/, detail::priority_tag<2> /*unused*/) const
{
return detail::view::map_value<std::unordered_map<K, V, H, E, A>>(*this);
return detail::view::map_value<std::unordered_map<K, basic_json_view, H, E, A>>(*this);
}
/// whether the from_json() of T's serializer reads views (see
+119 -27
View File
@@ -52,6 +52,11 @@ struct point
int x = 0;
int y = 0;
NLOHMANN_DEFINE_TYPE_INTRUSIVE(point, x, y)
friend bool operator==(const point& a, const point& b)
{
return a.x == b.x && a.y == b.y;
}
};
struct shape
@@ -184,6 +189,13 @@ namespace
{
template<typename T, typename View = json_view>
struct reads_view : nlohmann::detail::has_view_from_json<nlohmann::adl_serializer<T, void>, View, T> {};
// whether a member converts to the same T from the view and from the materialized value
template<typename T>
bool same(const json_view& v, const json& j, const char* key)
{
return v[key].get<T>() == j[key].get<T>();
}
} // namespace
TEST_CASE("json_view from_json")
@@ -215,39 +227,119 @@ TEST_CASE("json_view from_json")
static_assert(!nlohmann::is_json_view<int>::value, "is_json_view");
}
SECTION("the library's own conversions are not called with a view (and the probe compiles for them)")
SECTION("the library's own conversions read views")
{
// these go through materialize() (vectors, maps with string keys,
// strings, and numbers are converted by the view itself)
static_assert(!reads_view<std::list<int>>::value, "std types");
static_assert(!reads_view<std::deque<point>>::value, "std types");
static_assert(!reads_view<std::set<std::string>>::value, "std types");
static_assert(!reads_view<std::unordered_set<int>>::value, "std types");
static_assert(!reads_view<std::forward_list<int>>::value, "std types");
static_assert(!reads_view<std::valarray<double>>::value, "std types");
static_assert(!reads_view<std::array<int, 3>>::value, "std types");
static_assert(!reads_view<std::pair<int, std::string>>::value, "std types");
static_assert(!reads_view<std::tuple<int, bool, point>>::value, "std types");
static_assert(!reads_view<std::map<int, int>>::value, "std types");
static_assert(!reads_view<std::unordered_map<int, point>>::value, "std types");
static_assert(!reads_view<std::map<color, int>>::value, "std types");
static_assert(!reads_view<std::map<std::string, int>>::value, "std types");
static_assert(!reads_view<std::vector<point>>::value, "std types");
static_assert(!reads_view<std::vector<bool>>::value, "std types");
static_assert(!reads_view<std::vector<std::uint8_t>>::value, "std types");
static_assert(!reads_view<std::string>::value, "std types");
static_assert(!reads_view<int>::value, "std types");
static_assert(!reads_view<bool>::value, "std types");
static_assert(!reads_view<double>::value, "std types");
static_assert(!reads_view<std::nullptr_t>::value, "std types");
static_assert(!reads_view<json>::value, "std types");
static_assert(!reads_view<json::binary_t>::value, "std types");
static_assert(reads_view<std::list<int>>::value, "std types");
static_assert(reads_view<std::deque<point>>::value, "std types");
static_assert(reads_view<std::set<std::string>>::value, "std types");
static_assert(reads_view<std::unordered_set<int>>::value, "std types");
static_assert(reads_view<std::forward_list<int>>::value, "std types");
static_assert(reads_view<std::valarray<double>>::value, "std types");
static_assert(reads_view<std::array<int, 3>>::value, "std types");
static_assert(reads_view<std::pair<int, std::string>>::value, "std types");
static_assert(reads_view<std::tuple<int, bool, point>>::value, "std types");
static_assert(reads_view<std::map<int, int>>::value, "std types");
static_assert(reads_view<std::unordered_map<int, point>>::value, "std types");
static_assert(reads_view<std::map<color, int>>::value, "std types");
static_assert(reads_view<std::map<std::string, int>>::value, "std types");
static_assert(reads_view<std::vector<point>>::value, "std types");
static_assert(reads_view<std::vector<bool>>::value, "std types");
static_assert(reads_view<std::vector<std::uint8_t>>::value, "std types");
static_assert(reads_view<std::string>::value, "std types");
static_assert(reads_view<int>::value, "std types");
static_assert(reads_view<bool>::value, "std types");
static_assert(reads_view<double>::value, "std types");
static_assert(reads_view<std::nullptr_t>::value, "std types");
static_assert(!reads_view<json>::value, "get<BasicJsonType>() is materialize()");
static_assert(reads_view<json::binary_t>::value, "std types");
#ifdef JSON_HAS_CPP_17
static_assert(!reads_view<std::optional<point>>::value, "std types");
static_assert(reads_view<std::optional<point>>::value, "std types");
#endif
CHECK(true);
}
SECTION("std types: the same values as from the materialized value")
{
const json_document d = json_document::parse(R"({"ints": [3, 1, 2, 1], "strings": ["b", "a", "b"], "pairs": [[1, "x"], [2, "y"]],
"points": [{"x": 1, "y": 2}, {"x": 3, "y": 4}], "object": {"b": 1, "a": 2}, "triple": [1, true, {"x": 5, "y": 6}],
"colors": {"green": 1, "red": 2}, "bytes": [1, 2, 255], "nested": [[1, 2], [3]], "number": 1.5, "flag": true,
"text": "hello", "nothing": null, "mixed": [1, 2.5, -3, 4], "flags": [true, false, true]})");
const json j = d.root().materialize();
const json_view v = d.root();
CHECK(same<std::list<int>>(v, j, "ints"));
CHECK(same<std::deque<int>>(v, j, "ints"));
CHECK(same<std::set<int>>(v, j, "ints"));
CHECK(same<std::multiset<int>>(v, j, "ints"));
CHECK(same<std::unordered_set<std::string>>(v, j, "strings"));
CHECK(same<std::forward_list<int>>(v, j, "ints"));
CHECK(same<std::array<int, 4>>(v, j, "ints"));
CHECK(same<std::array<double, 4>>(v, j, "mixed"));
CHECK((v["pairs"][0].get<std::pair<int, std::string>>() == j["pairs"][0].get<std::pair<int, std::string>>()));
CHECK(same<std::vector<std::pair<int, std::string>>>(v, j, "pairs"));
CHECK(same<std::tuple<int, bool, point>>(v, j, "triple"));
CHECK(same<std::map<int, std::string>>(v, j, "pairs"));
CHECK(same<std::unordered_map<int, std::string>>(v, j, "pairs"));
CHECK(same<std::map<std::string, int>>(v, j, "object"));
CHECK(same<std::unordered_map<std::string, int>>(v, j, "object"));
CHECK(same<std::map<color, int>>(v, j, "colors"));
CHECK(same<std::vector<point>>(v, j, "points"));
CHECK(same<std::vector<int>>(v, j, "ints"));
CHECK(same<std::vector<bool>>(v, j, "flags"));
CHECK(same<std::vector<std::uint8_t>>(v, j, "bytes"));
CHECK(same<std::vector<std::vector<int>>>(v, j, "nested"));
CHECK(same<std::vector<json>>(v, j, "mixed"));
CHECK(same<double>(v, j, "number"));
CHECK(same<bool>(v, j, "flag"));
CHECK(same<std::string>(v, j, "text"));
CHECK(same<std::nullptr_t>(v, j, "nothing"));
CHECK(same<json>(v, j, "object"));
const std::valarray<int> va = v["ints"].get<std::valarray<int>>();
CHECK(va.size() == 4);
CHECK(va[0] == 3);
int c_array[2][2] = {{0, 0}, {0, 0}}; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays): the conversion to C arrays
v["nested"][0].get_to(c_array[0]);
CHECK(c_array[0][1] == 2);
#ifdef JSON_HAS_CPP_17
CHECK(v["points"][0].get<std::optional<point>>()->y == 2);
CHECK(!v["nothing"].get<std::optional<point>>().has_value());
#endif
// containers of views keep views of the elements
const std::vector<json_view> views = v["points"].get<std::vector<json_view>>();
CHECK(views.size() == 2);
CHECK(views[1]["x"].get<int>() == 3);
CHECK(v["object"].get<std::map<std::string, json_view>>().at("a").get<int>() == 2);
}
SECTION("std types: duplicate keys keep the last value, as from the materialized value")
{
const json_document d = json_document::parse(R"({"a": 1, "b": 2, "a": 3})");
CHECK((d.root().get<std::map<std::string, int>>() == d.root().materialize().get<std::map<std::string, int>>()));
CHECK(d.root().get<std::map<std::string, int>>().at("a") == 3);
CHECK(d.root().get<std::unordered_map<std::string, int>>().at("a") == 3);
}
#if !defined(JSON_NOEXCEPTION) && !JSON_DIAGNOSTICS
SECTION("std types: the same errors as from the materialized value")
{
const json_document d = json_document::parse(R"([[1, "x"], {"a": 1}, "text", [1], 5, null])");
const json j = d.root().materialize();
for (std::size_t i = 0; i < 6; ++i)
{
CAPTURE(i)
const json_view v = d.root()[i];
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::list<int>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::list<int>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::forward_list<int>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::forward_list<int>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::array<int, 2>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::array<int, 2>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::pair<int, std::string>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::pair<int, std::string>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::map<std::string, int>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::map<std::string, int>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::map<int, int>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::map<int, int>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::vector<std::uint8_t>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::vector<std::uint8_t>>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<json::binary_t>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<json::binary_t>()); }));
CHECK(exception_of_call([&] { static_cast<void>(v.get<std::valarray<int>>()); }) == exception_of_call([&] { static_cast<void>(j[i].get<std::valarray<int>>()); }));
}
}
#endif
SECTION("macro types")
{
const json_document d = json_document::parse(R"({"name": "s", "points": [{"x": 1, "y": 2}, {"x": 3, "y": 4}],