mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 14:10:31 +00:00
* Store maps with enum keys as objects (opt-in) Maps with enum keys, such as std::map<E, T>, are stored as arrays of [key, value] pairs, because enums are not convertible to the string type of object keys - even if NLOHMANN_JSON_SERIALIZE_ENUM maps them to strings (#4378). The new JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS macro stores them as objects instead, converting each key with the enum's to_json. It applies to any map-like type with enum keys (std::map with any comparator, std::unordered_map, ...). A key that does not convert to a string throws type_error.302, and two keys converting to the same string throw the new type_error.318, rather than losing an entry. The macro changes the output of inline functions, so it is part of the ABI tag (_ekmo). Reading needs no macro: std::map and std::unordered_map with enum keys are now also read from objects, converting each key with the enum's from_json. That input was rejected before, and arrays of pairs are still read, so data written either way can be read. This supersedes #4531, which first proposed storing these maps as objects. Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep multimaps with enum keys as arrays of pairs With JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, is_enum_keyed_map also matched std::multimap and std::unordered_multimap. Storing them as objects throws type_error.318 as soon as a key occurs twice, which is the normal case for a multimap, so such values could no longer be serialized at all once the macro was enabled, although they are stored losslessly as arrays of [key, value] pairs without it. Exclude maps with non-unique keys from is_enum_keyed_map. They are detected by insert(value_type) returning an iterator rather than a pair<iterator, bool>. Map-like types without such an insert() are still treated as before. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the default enum-keyed map tests out of unit-conversions.cpp The Windows clang 20.1.8 job (MinGW, Debug) failed to link test-conversions_cpp17 with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against .rdata": the object file of unit-conversions.cpp was already close to the limit, and the new "maps with enum keys" test case pushed it over. windows.yml asks to keep these objects small by splitting test files. Move the test case unchanged into unit-enum_keyed_maps_default.cpp, with the three enums it needs. It still honors a -D flag for JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, as before. unit-conversions.cpp is back to its state on develop. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Build the enum-keyed map test object instead of parsing it ci_test_diagnostic_positions failed in unit-enum_keyed_maps_default.cpp: with JSON_DIAGNOSTIC_POSITIONS, a parsed value adds its byte range to the exception message ("(bytes 0-7) type must be array, but is object"), so the exact-message checks did not match. Build the object in memory, like unit-custom-array-type.cpp does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu>
635 lines
24 KiB
C++
635 lines
24 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 <algorithm> // transform
|
|
#include <array> // array
|
|
#include <forward_list> // forward_list
|
|
#include <iterator> // inserter, front_inserter, end
|
|
#include <map> // map
|
|
#include <string> // string
|
|
#include <tuple> // tuple, make_tuple
|
|
#include <type_traits> // is_arithmetic, is_same, is_enum, underlying_type, is_convertible
|
|
#include <unordered_map> // unordered_map
|
|
#include <utility> // pair, declval
|
|
#include <valarray> // valarray
|
|
#include <vector> // vector
|
|
|
|
#include <nlohmann/detail/exceptions.hpp>
|
|
#include <nlohmann/detail/macro_scope.hpp>
|
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
#include <nlohmann/detail/meta/identity_tag.hpp>
|
|
#include <nlohmann/detail/meta/std_fs.hpp>
|
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
#include <nlohmann/detail/string_concat.hpp>
|
|
#include <nlohmann/detail/value_t.hpp>
|
|
|
|
// include after macro_scope.hpp
|
|
#ifdef JSON_HAS_CPP_17
|
|
#include <optional> // optional
|
|
#endif
|
|
|
|
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
#include <string_view> // u8string_view
|
|
#endif
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename std::nullptr_t& n)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_null()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be null, but is ", j.type_name()), &j));
|
|
}
|
|
n = nullptr;
|
|
}
|
|
|
|
#ifdef JSON_HAS_CPP_17
|
|
template < typename BasicJsonType, typename T,
|
|
typename std::enable_if < !nlohmann::detail::is_basic_json<T>::value, int >::type = 0 >
|
|
void from_json(const BasicJsonType& j, std::optional<T>& opt)
|
|
{
|
|
if (j.is_null())
|
|
{
|
|
opt = std::nullopt;
|
|
}
|
|
else
|
|
{
|
|
opt.emplace(j.template get<T>());
|
|
}
|
|
}
|
|
#endif // JSON_HAS_CPP_17
|
|
|
|
// 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,
|
|
int > = 0 >
|
|
void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val)
|
|
{
|
|
switch (static_cast<value_t>(j))
|
|
{
|
|
case value_t::number_unsigned:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
|
break;
|
|
}
|
|
case value_t::number_integer:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
|
break;
|
|
}
|
|
case value_t::number_float:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
|
break;
|
|
}
|
|
|
|
case value_t::null:
|
|
case value_t::object:
|
|
case value_t::array:
|
|
case value_t::string:
|
|
case value_t::boolean:
|
|
case value_t::binary:
|
|
case value_t::discarded:
|
|
default:
|
|
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
|
}
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_boolean()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be boolean, but is ", j.type_name()), &j));
|
|
}
|
|
b = *j.template get_ptr<const typename BasicJsonType::boolean_t*>();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
|
}
|
|
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
}
|
|
|
|
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
|
|
&& !is_json_ref<StringType>::value, int > = 0 >
|
|
inline void from_json(const BasicJsonType& j, StringType& s)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val)
|
|
{
|
|
get_arithmetic_value(j, val);
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val)
|
|
{
|
|
get_arithmetic_value(j, val);
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val)
|
|
{
|
|
get_arithmetic_value(j, val);
|
|
}
|
|
|
|
#if !JSON_DISABLE_ENUM_SERIALIZATION
|
|
template<typename BasicJsonType, typename EnumType,
|
|
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
|
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;
|
|
value_type val;
|
|
get_arithmetic_value(j, val);
|
|
e = static_cast<EnumType>(static_cast<underlying_type>(val));
|
|
}
|
|
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
|
|
|
// 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>
|
|
inline void from_json(const BasicJsonType& j, std::forward_list<T, Allocator>& l)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
l.clear();
|
|
std::transform(j.rbegin(), j.rend(),
|
|
std::front_inserter(l), [](const BasicJsonType & i)
|
|
{
|
|
return i.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>
|
|
inline void from_json(const BasicJsonType& j, std::valarray<T>& l)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
l.resize(j.size());
|
|
std::transform(j.begin(), j.end(), std::begin(l),
|
|
[](const BasicJsonType & elem)
|
|
{
|
|
return elem.template get<T>();
|
|
});
|
|
}
|
|
|
|
// element is not itself a C array: read it directly
|
|
template<typename BasicJsonType, typename T>
|
|
auto from_json_c_array_element(const BasicJsonType& j, T& e)
|
|
-> decltype(e = j.template get<T>(), void())
|
|
{
|
|
e = j.template get<T>();
|
|
}
|
|
|
|
// element is itself a C array: recurse one dimension at a time, so any rank is supported
|
|
template<typename BasicJsonType, typename T, std::size_t N>
|
|
void from_json_c_array_element(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
{
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
{
|
|
from_json_c_array_element(j.at(i), arr[i]);
|
|
}
|
|
}
|
|
|
|
template<typename BasicJsonType, typename T, std::size_t N>
|
|
auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
-> decltype(j.template get<typename std::remove_all_extents<T>::type>(), void())
|
|
{
|
|
from_json_c_array_element(j, arr);
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
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*>();
|
|
}
|
|
|
|
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*/)
|
|
-> decltype(j.template get<T>(), void())
|
|
{
|
|
for (std::size_t i = 0; i < N; ++i)
|
|
{
|
|
arr[i] = j.at(i).template get<T>();
|
|
}
|
|
}
|
|
|
|
// reserve() is called through this pair (modeled on from_json_object_reserve)
|
|
// so from_json_array_impl below has a single body for both ConstructibleArrayType
|
|
// that support reserve() and those that don't.
|
|
template<typename ConstructibleArrayType>
|
|
auto from_json_array_reserve(ConstructibleArrayType& arr, typename ConstructibleArrayType::size_type size, priority_tag<1> /*unused*/)
|
|
-> decltype(arr.reserve(size), void())
|
|
{
|
|
arr.reserve(size);
|
|
}
|
|
|
|
template<typename ConstructibleArrayType>
|
|
void from_json_array_reserve(ConstructibleArrayType& /*arr*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
|
{}
|
|
|
|
template<typename BasicJsonType, typename ConstructibleArrayType,
|
|
enable_if_t<
|
|
std::is_assignable<ConstructibleArrayType&, ConstructibleArrayType>::value,
|
|
int> = 0>
|
|
auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/)
|
|
-> decltype(
|
|
j.template get<typename ConstructibleArrayType::value_type>(),
|
|
void())
|
|
{
|
|
using std::end;
|
|
|
|
ConstructibleArrayType ret;
|
|
from_json_array_reserve(ret, j.size(), priority_tag<1> {});
|
|
std::transform(j.begin(), j.end(),
|
|
std::inserter(ret, end(ret)), [](const BasicJsonType & i)
|
|
{
|
|
// get<BasicJsonType>() returns *this, this won't call a from_json
|
|
// method when value_type is BasicJsonType
|
|
return i.template get<typename ConstructibleArrayType::value_type>();
|
|
});
|
|
arr = std::move(ret);
|
|
}
|
|
|
|
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_basic_json<ConstructibleArrayType>::value,
|
|
int > = 0 >
|
|
auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr)
|
|
-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}),
|
|
j.template get<typename ConstructibleArrayType::value_type>(),
|
|
void())
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
from_json_array_impl(j, arr, priority_tag<3> {});
|
|
}
|
|
|
|
template < typename BasicJsonType, typename T, std::size_t... Idx >
|
|
std::array<T, sizeof...(Idx)> from_json_inplace_array_impl(const BasicJsonType& j,
|
|
identity_tag<std::array<T, sizeof...(Idx)>> /*unused*/, index_sequence<Idx...> /*unused*/)
|
|
{
|
|
return { { j.at(Idx).template get<T>()... } };
|
|
}
|
|
|
|
template < typename BasicJsonType, typename T, std::size_t N >
|
|
auto from_json(const BasicJsonType& j, identity_tag<std::array<T, N>> tag)
|
|
-> decltype(from_json_inplace_array_impl(j, tag, make_index_sequence<N> {}))
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
return from_json_inplace_array_impl(j, tag, make_index_sequence<N> {});
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_binary()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be binary, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
bin = *j.template get_ptr<const typename BasicJsonType::binary_t*>();
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
enable_if_t < is_compatible_binary_type<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*>());
|
|
}
|
|
else if (j.is_array())
|
|
{
|
|
from_json_array_impl(j, bin, priority_tag<3> {});
|
|
}
|
|
else
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be binary or array, but is ", j.type_name()), &j));
|
|
}
|
|
}
|
|
|
|
template<typename ConstructibleObjectType>
|
|
auto from_json_object_reserve(ConstructibleObjectType& obj, typename ConstructibleObjectType::size_type size, priority_tag<1> /*unused*/)
|
|
-> decltype(obj.reserve(size), void())
|
|
{
|
|
obj.reserve(size);
|
|
}
|
|
|
|
template<typename ConstructibleObjectType>
|
|
inline void from_json_object_reserve(ConstructibleObjectType& /*obj*/, std::size_t /*size*/, priority_tag<0> /*unused*/)
|
|
{}
|
|
|
|
template<typename BasicJsonType, typename ConstructibleObjectType,
|
|
enable_if_t<is_constructible_object_type<BasicJsonType, ConstructibleObjectType>::value, int> = 0>
|
|
inline void from_json(const BasicJsonType& j, ConstructibleObjectType& obj)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_object()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be object, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
ConstructibleObjectType ret;
|
|
const auto* inner_object = j.template get_ptr<const typename BasicJsonType::object_t*>();
|
|
from_json_object_reserve(ret, inner_object->size(), priority_tag<1> {});
|
|
for (const auto& p : *inner_object)
|
|
{
|
|
ret.emplace(p.first, p.second.template get<typename ConstructibleObjectType::mapped_type>());
|
|
}
|
|
obj = std::move(ret);
|
|
}
|
|
|
|
// overload for arithmetic types, not chosen for basic_json template arguments
|
|
// (BooleanType, etc.)
|
|
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,
|
|
int > = 0 >
|
|
inline void from_json(const BasicJsonType& j, ArithmeticType& val)
|
|
{
|
|
switch (static_cast<value_t>(j))
|
|
{
|
|
case value_t::number_unsigned:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_unsigned_t*>());
|
|
break;
|
|
}
|
|
case value_t::number_integer:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_integer_t*>());
|
|
break;
|
|
}
|
|
case value_t::number_float:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::number_float_t*>());
|
|
break;
|
|
}
|
|
case value_t::boolean:
|
|
{
|
|
val = static_cast<ArithmeticType>(*j.template get_ptr<const typename BasicJsonType::boolean_t*>());
|
|
break;
|
|
}
|
|
|
|
case value_t::null:
|
|
case value_t::object:
|
|
case value_t::array:
|
|
case value_t::string:
|
|
case value_t::binary:
|
|
case value_t::discarded:
|
|
default:
|
|
JSON_THROW(type_error::create(302, concat("type must be number, but is ", j.type_name()), &j));
|
|
}
|
|
}
|
|
|
|
template<typename BasicJsonType, typename Type>
|
|
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType&& j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<0> /*unused*/)
|
|
{
|
|
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
|
|
}
|
|
|
|
template<typename BasicJsonType, typename Type,
|
|
detail::enable_if_t<detail::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>();
|
|
}
|
|
|
|
template<typename BasicJsonType, typename Type,
|
|
detail::enable_if_t<std::is_arithmetic<uncvref_t<Type>>::value, int> = 0>
|
|
detail::uncvref_t<Type> from_json_tuple_get_impl(BasicJsonType && j, detail::identity_tag<Type> /*unused*/, detail::priority_tag<2> /*unused*/)
|
|
{
|
|
return std::forward<BasicJsonType>(j).template get<detail::uncvref_t<Type>>();
|
|
}
|
|
|
|
template<std::size_t PTagValue, typename BasicJsonType, typename... Types>
|
|
using tuple_type = std::tuple < decltype(from_json_tuple_get_impl(std::declval<BasicJsonType>(), detail::identity_tag<Types> {}, detail::priority_tag<PTagValue> {}))... >;
|
|
|
|
template<std::size_t PTagValue, typename... Args, typename BasicJsonType, std::size_t... Idx>
|
|
tuple_type<PTagValue, const BasicJsonType&, Args...> from_json_tuple_impl_base(const BasicJsonType& j, index_sequence<Idx...> /*unused*/)
|
|
{
|
|
return tuple_type<PTagValue, const BasicJsonType&, Args...>(from_json_tuple_get_impl(j.at(Idx), detail::identity_tag<Args> {}, detail::priority_tag<PTagValue> {})...);
|
|
}
|
|
|
|
template<std::size_t PTagValue, typename BasicJsonType>
|
|
std::tuple<> from_json_tuple_impl_base(const BasicJsonType& /*unused*/, index_sequence<> /*unused*/)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
template < typename BasicJsonType, class A1, class A2 >
|
|
std::pair<A1, A2> from_json_tuple_impl(const BasicJsonType& j, identity_tag<std::pair<A1, A2>> /*unused*/, priority_tag<0> /*unused*/)
|
|
{
|
|
return {j.at(0).template get<A1>(),
|
|
j.at(1).template get<A2>()};
|
|
}
|
|
|
|
template<typename BasicJsonType, typename A1, typename A2>
|
|
inline void from_json_tuple_impl(const BasicJsonType& j, std::pair<A1, A2>& p, priority_tag<1> /*unused*/)
|
|
{
|
|
p = from_json_tuple_impl(j, identity_tag<std::pair<A1, A2>> {}, priority_tag<0> {});
|
|
}
|
|
|
|
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,
|
|
"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...> {});
|
|
}
|
|
|
|
template<typename BasicJsonType, typename... Args>
|
|
inline void from_json_tuple_impl(const BasicJsonType& j, std::tuple<Args...>& t, priority_tag<3> /*unused*/)
|
|
{
|
|
t = from_json_tuple_impl_base<2, Args...>(j, index_sequence_for<Args...> {});
|
|
}
|
|
|
|
template<typename BasicJsonType, typename TupleRelated>
|
|
auto from_json(const BasicJsonType& j, TupleRelated&& t)
|
|
-> decltype(from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {}))
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
|
|
return from_json_tuple_impl(j, std::forward<TupleRelated>(t), priority_tag<3> {});
|
|
}
|
|
|
|
// shared body for std::map/std::unordered_map with a non-string Key: both
|
|
// containers are read from an array of [key, value] pairs the same way
|
|
template<typename BasicJsonType, typename MapType>
|
|
void from_json_pair_array_to_map(const BasicJsonType& j, MapType& m)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", j.type_name()), &j));
|
|
}
|
|
m.clear();
|
|
for (const auto& p : j)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!p.is_array()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be array, but is ", p.type_name()), &p));
|
|
}
|
|
m.emplace(p.at(0).template get<typename MapType::key_type>(), p.at(1).template get<typename MapType::mapped_type>());
|
|
}
|
|
}
|
|
|
|
// 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>
|
|
inline bool from_json_enum_keyed_object(const BasicJsonType& j, Map& m, std::true_type /*key is enum*/)
|
|
{
|
|
if (!j.is_object())
|
|
{
|
|
return false;
|
|
}
|
|
m.clear();
|
|
for (const auto& p : *j.template get_ptr<const typename BasicJsonType::object_t*>())
|
|
{
|
|
m.emplace(BasicJsonType(p.first).template get<typename Map::key_type>(), p.second.template get<typename Map::mapped_type>());
|
|
}
|
|
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*/)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator,
|
|
typename = enable_if_t < !std::is_constructible <
|
|
typename 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
|
|
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
|
{
|
|
return;
|
|
}
|
|
from_json_pair_array_to_map(j, m);
|
|
}
|
|
|
|
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 >>
|
|
void from_json(const BasicJsonType& j, std::unordered_map<Key, Value, Hash, KeyEqual, Allocator>& m)
|
|
{
|
|
// NOLINTNEXTLINE(modernize-type-traits) we use C++11
|
|
if (from_json_enum_keyed_object(j, m, std::is_enum<Key> {}))
|
|
{
|
|
return;
|
|
}
|
|
from_json_pair_array_to_map(j, m);
|
|
}
|
|
|
|
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
|
|
// Workaround for MSVC 19.51 (and possibly later): in large cpp files, the compiler may fail to resolve with generic has_from_json (issue #4996)
|
|
template<typename BasicJsonType>
|
|
struct has_from_json<BasicJsonType, std_fs::path, void> : std::true_type {};
|
|
|
|
template<typename BasicJsonType>
|
|
inline void from_json(const BasicJsonType& j, std_fs::path& p)
|
|
{
|
|
if (JSON_HEDLEY_UNLIKELY(!j.is_string()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", j.type_name()), &j));
|
|
}
|
|
const auto& s = *j.template get_ptr<const typename BasicJsonType::string_t*>();
|
|
// 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.
|
|
#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201907L)
|
|
p = std_fs::path(std::u8string_view(reinterpret_cast<const char8_t*>(s.data()), s.size()));
|
|
#else
|
|
p = std_fs::u8path(s); // accepts UTF-8 encoded std::string in C++17, deprecated in C++20
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
struct from_json_fn
|
|
{
|
|
template<typename BasicJsonType, typename T>
|
|
auto operator()(const BasicJsonType& j, T&& val) const
|
|
noexcept(noexcept(from_json(j, std::forward<T>(val))))
|
|
-> decltype(from_json(j, std::forward<T>(val)))
|
|
{
|
|
return from_json(j, std::forward<T>(val));
|
|
}
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
#ifndef JSON_HAS_CPP_17
|
|
/// namespace to hold default `from_json` function
|
|
/// to see why this is required:
|
|
/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
|
|
namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces,misc-anonymous-namespace-in-header)
|
|
{
|
|
#endif
|
|
JSON_INLINE_VARIABLE constexpr const auto& from_json = // NOLINT(misc-definitions-in-headers)
|
|
detail::static_const<detail::from_json_fn>::value;
|
|
#ifndef JSON_HAS_CPP_17
|
|
} // namespace
|
|
#endif
|
|
|
|
NLOHMANN_JSON_NAMESPACE_END
|