mirror of
https://github.com/nlohmann/json.git
synced 2026-10-05 06:00:29 +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>
594 lines
22 KiB
C++
594 lines
22 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 <nlohmann/detail/macro_scope.hpp> // JSON_HAS_CPP_17
|
|
#ifdef JSON_HAS_CPP_17
|
|
#include <optional> // optional
|
|
#endif
|
|
|
|
#include <algorithm> // copy
|
|
#include <iterator> // begin, end
|
|
#include <memory> // allocator_traits
|
|
#include <string> // basic_string, char_traits
|
|
#include <tuple> // tuple, get
|
|
#include <type_traits> // is_same, is_constructible, is_floating_point, is_enum, underlying_type
|
|
#include <utility> // move, forward, declval, pair
|
|
#include <valarray> // valarray
|
|
#include <vector> // vector
|
|
|
|
#include <nlohmann/detail/exceptions.hpp>
|
|
#include <nlohmann/detail/iterators/iteration_proxy.hpp>
|
|
#include <nlohmann/detail/meta/cpp_future.hpp>
|
|
#include <nlohmann/detail/meta/std_fs.hpp>
|
|
#include <nlohmann/detail/meta/type_traits.hpp>
|
|
#include <nlohmann/detail/value_t.hpp>
|
|
|
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
|
namespace detail
|
|
{
|
|
|
|
//////////////////
|
|
// constructors //
|
|
//////////////////
|
|
|
|
/*
|
|
* Note all external_constructor<>::construct functions need to call
|
|
* j.m_data.m_value.destroy(j.m_data.m_type) to avoid a memory leak in case j contains an
|
|
* allocated value (e.g., a string). See bug issue
|
|
* https://github.com/nlohmann/json/issues/2865 for more information.
|
|
*/
|
|
|
|
template<value_t> struct external_constructor;
|
|
|
|
template<>
|
|
struct external_constructor<value_t::boolean>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::boolean;
|
|
j.m_data.m_value = b;
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::string>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::string;
|
|
j.m_data.m_value = s;
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::string;
|
|
j.m_data.m_value = std::move(s);
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleStringType,
|
|
enable_if_t < !std::is_same<CompatibleStringType, typename BasicJsonType::string_t>::value,
|
|
int > = 0 >
|
|
static void construct(BasicJsonType& j, const CompatibleStringType& str)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::string;
|
|
j.m_data.m_value.string = j.template create<typename BasicJsonType::string_t>(str);
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::binary>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::binary;
|
|
j.m_data.m_value = typename BasicJsonType::binary_t(b);
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::binary;
|
|
j.m_data.m_value = typename BasicJsonType::binary_t(std::move(b));
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::number_float>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::number_float;
|
|
j.m_data.m_value = val;
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::number_unsigned>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::number_unsigned;
|
|
j.m_data.m_value = val;
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::number_integer>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::number_integer;
|
|
j.m_data.m_value = val;
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::array>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value = arr;
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value = std::move(arr);
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
enable_if_t < !std::is_same<CompatibleArrayType, typename BasicJsonType::array_t>::value
|
|
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
|
&& !is_compatible_range_view<CompatibleArrayType>::value
|
|
#endif
|
|
, int > = 0 >
|
|
static void construct(BasicJsonType& j, const CompatibleArrayType& arr)
|
|
{
|
|
using std::begin;
|
|
using std::end;
|
|
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value.array = j.template create<typename BasicJsonType::array_t>(begin(arr), end(arr));
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, const std::vector<bool>& arr)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value = value_t::array;
|
|
j.m_data.m_value.array->reserve(arr.size());
|
|
for (const bool x : arr)
|
|
{
|
|
j.m_data.m_value.array->push_back(x);
|
|
j.set_parent(j.m_data.m_value.array->back());
|
|
}
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType, typename T,
|
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
|
static void construct(BasicJsonType& j, const std::valarray<T>& arr)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value = value_t::array;
|
|
j.m_data.m_value.array->resize(arr.size());
|
|
std::copy(std::begin(arr), std::end(arr), j.m_data.m_value.array->begin());
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
|
enable_if_t<is_compatible_range_view<std::remove_cvref_t<CompatibleArrayType>>::value, int> = 0>
|
|
static void construct(BasicJsonType& j, CompatibleArrayType && arr)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::array;
|
|
j.m_data.m_value = value_t::array;
|
|
for (auto&& x : std::forward<CompatibleArrayType>(arr))
|
|
{
|
|
j.m_data.m_value.array->push_back(x);
|
|
}
|
|
// set the parents only once all elements are in place: a push_back
|
|
// that reallocates moves the earlier elements, which does not keep
|
|
// their parent pointers
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
#endif
|
|
};
|
|
|
|
template<>
|
|
struct external_constructor<value_t::object>
|
|
{
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::object;
|
|
j.m_data.m_value = obj;
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
|
{
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::object;
|
|
j.m_data.m_value = std::move(obj);
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleObjectType,
|
|
enable_if_t < !std::is_same<CompatibleObjectType, typename BasicJsonType::object_t>::value, int > = 0 >
|
|
static void construct(BasicJsonType& j, const CompatibleObjectType& obj)
|
|
{
|
|
using std::begin;
|
|
using std::end;
|
|
|
|
j.m_data.m_value.destroy(j.m_data.m_type);
|
|
j.m_data.m_type = value_t::object;
|
|
j.m_data.m_value.object = j.template create<typename BasicJsonType::object_t>(begin(obj), end(obj));
|
|
j.set_parents();
|
|
j.assert_invariant();
|
|
}
|
|
};
|
|
|
|
/////////////
|
|
// to_json //
|
|
/////////////
|
|
|
|
#ifdef JSON_HAS_CPP_17
|
|
template<typename BasicJsonType, typename T,
|
|
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
|
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
|
{
|
|
if (opt.has_value())
|
|
{
|
|
// explicit construction, as the conversion from a basic_json with a different
|
|
// string type is explicit if JSON_USE_IMPLICIT_CONVERSIONS is 0 (#2649)
|
|
j = BasicJsonType(*opt);
|
|
}
|
|
else
|
|
{
|
|
j = nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template<typename BasicJsonType, typename T,
|
|
enable_if_t<std::is_same<T, typename BasicJsonType::boolean_t>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, T b) noexcept
|
|
{
|
|
external_constructor<value_t::boolean>::construct(j, b);
|
|
}
|
|
|
|
template < typename BasicJsonType, typename BoolRef,
|
|
enable_if_t <
|
|
((std::is_same<std::vector<bool>::reference, BoolRef>::value
|
|
&& !std::is_same <std::vector<bool>::reference, typename BasicJsonType::boolean_t&>::value)
|
|
|| (std::is_same<std::vector<bool>::const_reference, BoolRef>::value
|
|
&& !std::is_same <detail::uncvref_t<std::vector<bool>::const_reference>,
|
|
typename BasicJsonType::boolean_t >::value))
|
|
&& std::is_convertible<const BoolRef&, typename BasicJsonType::boolean_t>::value, int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const BoolRef& b) noexcept
|
|
{
|
|
external_constructor<value_t::boolean>::construct(j, static_cast<typename BasicJsonType::boolean_t>(b));
|
|
}
|
|
|
|
template<typename BasicJsonType, typename CompatibleString,
|
|
enable_if_t<std::is_constructible<typename BasicJsonType::string_t, CompatibleString>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, const CompatibleString& s)
|
|
{
|
|
external_constructor<value_t::string>::construct(j, s);
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s)
|
|
{
|
|
external_constructor<value_t::string>::construct(j, std::move(s));
|
|
}
|
|
|
|
template<typename BasicJsonType, typename FloatType,
|
|
enable_if_t<std::is_floating_point<FloatType>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, FloatType val) noexcept
|
|
{
|
|
external_constructor<value_t::number_float>::construct(j, static_cast<typename BasicJsonType::number_float_t>(val));
|
|
}
|
|
|
|
template<typename BasicJsonType, typename CompatibleNumberUnsignedType,
|
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_unsigned_t, CompatibleNumberUnsignedType>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept
|
|
{
|
|
external_constructor<value_t::number_unsigned>::construct(j, static_cast<typename BasicJsonType::number_unsigned_t>(val));
|
|
}
|
|
|
|
template<typename BasicJsonType, typename CompatibleNumberIntegerType,
|
|
enable_if_t<is_compatible_integer_type<typename BasicJsonType::number_integer_t, CompatibleNumberIntegerType>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept
|
|
{
|
|
external_constructor<value_t::number_integer>::construct(j, static_cast<typename BasicJsonType::number_integer_t>(val));
|
|
}
|
|
|
|
#if !JSON_DISABLE_ENUM_SERIALIZATION
|
|
template<typename BasicJsonType, typename EnumType,
|
|
enable_if_t<std::is_enum<EnumType>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, EnumType e) noexcept
|
|
{
|
|
using underlying_type = typename std::underlying_type<EnumType>::type;
|
|
static constexpr value_t integral_value_t = std::is_unsigned<underlying_type>::value ? value_t::number_unsigned : value_t::number_integer;
|
|
external_constructor<integral_value_t>::construct(j, static_cast<underlying_type>(e));
|
|
}
|
|
#endif // JSON_DISABLE_ENUM_SERIALIZATION
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, const std::vector<bool>& e)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, e);
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
enable_if_t < is_compatible_array_type<BasicJsonType,
|
|
CompatibleArrayType>::value&&
|
|
!is_compatible_object_type<BasicJsonType, CompatibleArrayType>::value&&
|
|
!is_compatible_string_type<BasicJsonType, CompatibleArrayType>::value&&
|
|
!std::is_same<typename BasicJsonType::binary_t, CompatibleArrayType>::value&&
|
|
!is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value&&
|
|
!is_basic_json<CompatibleArrayType>::value
|
|
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
|
&& !is_compatible_range_view<CompatibleArrayType>::value
|
|
#endif
|
|
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
|
&& !is_enum_keyed_map<CompatibleArrayType>::value
|
|
#endif
|
|
,
|
|
int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, arr);
|
|
}
|
|
|
|
#if JSON_HAS_RANGE_VIEW_CONVERSION
|
|
template < typename BasicJsonType, typename T,
|
|
enable_if_t < is_compatible_range_view<std::remove_cvref_t<T>>::value
|
|
&& !is_compatible_string_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
|
&& !is_compatible_object_type<BasicJsonType, std::remove_cvref_t<T>>::value
|
|
&& !is_basic_json<std::remove_cvref_t<T>>::value, int > = 0 >
|
|
inline void to_json(BasicJsonType& j, T && arr)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, std::forward<T>(arr));
|
|
}
|
|
#endif
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
|
|
{
|
|
external_constructor<value_t::binary>::construct(j, bin);
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleArrayType,
|
|
enable_if_t < is_compatible_binary_type<BasicJsonType, CompatibleArrayType>::value,
|
|
int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const CompatibleArrayType& bin)
|
|
{
|
|
external_constructor<value_t::binary>::construct(j, typename BasicJsonType::binary_t(bin));
|
|
}
|
|
|
|
template<typename BasicJsonType, typename T,
|
|
enable_if_t<std::is_convertible<T, BasicJsonType>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, const std::valarray<T>& arr)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, std::move(arr));
|
|
}
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, std::move(arr));
|
|
}
|
|
|
|
template < typename BasicJsonType, typename CompatibleObjectType,
|
|
enable_if_t < is_compatible_object_type<BasicJsonType, CompatibleObjectType>::value&& !is_basic_json<CompatibleObjectType>::value, int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const CompatibleObjectType& obj)
|
|
{
|
|
external_constructor<value_t::object>::construct(j, obj);
|
|
}
|
|
|
|
#if JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS
|
|
// store a map with enum keys as an object, using the enum's own to_json for the
|
|
// keys (e.g., from NLOHMANN_JSON_SERIALIZE_ENUM); without the macro, such maps
|
|
// are stored as arrays of [key, value] pairs
|
|
template < typename BasicJsonType, typename EnumKeyedMap,
|
|
enable_if_t < is_enum_keyed_map<EnumKeyedMap>::value&& !is_basic_json<EnumKeyedMap>::value, int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|
{
|
|
typename BasicJsonType::object_t obj;
|
|
for (const auto& p : map)
|
|
{
|
|
BasicJsonType key = p.first;
|
|
if (JSON_HEDLEY_UNLIKELY(!key.is_string()))
|
|
{
|
|
JSON_THROW(type_error::create(302, concat("type must be string, but is ", key.type_name()), &key));
|
|
}
|
|
|
|
auto& key_string = *key.template get_ptr<typename BasicJsonType::string_t*>();
|
|
if (JSON_HEDLEY_UNLIKELY(!obj.emplace(key_string, BasicJsonType(p.second)).second))
|
|
{
|
|
JSON_THROW(type_error::create(318, concat("duplicate object key '", key_string, "'"), &key));
|
|
}
|
|
}
|
|
external_constructor<value_t::object>::construct(j, std::move(obj));
|
|
}
|
|
#endif
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj)
|
|
{
|
|
external_constructor<value_t::object>::construct(j, std::move(obj));
|
|
}
|
|
|
|
template <
|
|
typename BasicJsonType, typename T, std::size_t N,
|
|
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
|
|
const T(&)[N]>::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays)
|
|
{
|
|
external_constructor<value_t::array>::construct(j, arr);
|
|
}
|
|
|
|
template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible<BasicJsonType, T1>::value&& std::is_constructible<BasicJsonType, T2>::value, int > = 0 >
|
|
inline void to_json(BasicJsonType& j, const std::pair<T1, T2>& p)
|
|
{
|
|
j = { p.first, p.second };
|
|
}
|
|
|
|
// for https://github.com/nlohmann/json/pull/1134
|
|
template<typename BasicJsonType, typename T,
|
|
enable_if_t<std::is_same<T, iteration_proxy_value<typename BasicJsonType::iterator>>::value, int> = 0>
|
|
inline void to_json(BasicJsonType& j, const T& b)
|
|
{
|
|
j = { {b.key(), b.value()} };
|
|
}
|
|
|
|
template<typename BasicJsonType, typename Tuple, std::size_t... Idx>
|
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<Idx...> /*unused*/)
|
|
{
|
|
j = { std::get<Idx>(t)... };
|
|
}
|
|
|
|
// A one-element braced list does not reliably wrap its element: with
|
|
// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize
|
|
// std::tuple<int>{5} as 5 rather than [5], and some compilers (e.g., Apple clang
|
|
// 15 and 16) copy an element that is itself a basic_json even without it, so
|
|
// std::tuple<json>{true} became true rather than [true]. Build what the default
|
|
// deduction builds instead: an object if the element is a [string, value] pair,
|
|
// a one-element array otherwise.
|
|
template<typename BasicJsonType, typename Tuple>
|
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/)
|
|
{
|
|
BasicJsonType element(std::get<0>(t));
|
|
// same test as the initializer-list constructor, including the cast that
|
|
// keeps a string type constructible from 0 from selecting operator[](key)
|
|
const bool is_member = element.is_array() && element.size() == 2
|
|
&& element[static_cast<typename BasicJsonType::size_type>(0)].is_string();
|
|
if (is_member)
|
|
{
|
|
j = BasicJsonType::object({std::move(element)});
|
|
}
|
|
else
|
|
{
|
|
j = BasicJsonType::array({std::move(element)});
|
|
}
|
|
}
|
|
|
|
template<typename BasicJsonType, typename Tuple>
|
|
inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)
|
|
{
|
|
using array_t = typename BasicJsonType::array_t;
|
|
j = array_t();
|
|
}
|
|
|
|
template<typename BasicJsonType, typename T, enable_if_t<is_constructible_tuple<BasicJsonType, T>::value, int > = 0>
|
|
inline void to_json(BasicJsonType& j, const T& t)
|
|
{
|
|
to_json_tuple_impl(j, t, make_index_sequence<std::tuple_size<T>::value> {});
|
|
}
|
|
|
|
#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM
|
|
#if defined(__cpp_lib_char8_t)
|
|
template<typename BasicJsonType, typename Tr, typename Allocator>
|
|
inline void to_json(BasicJsonType& j, const std::basic_string<char8_t, Tr, Allocator>& s)
|
|
{
|
|
using OtherAllocator = typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
|
j = std::basic_string<char, std::char_traits<char>, OtherAllocator>(s.begin(), s.end(), s.get_allocator());
|
|
}
|
|
#endif
|
|
|
|
// Workaround for MSVC 19.51 (and possibly later): in large cpp files, the compiler may fail to resolve with generic has_to_json (issue #4996)
|
|
template<typename BasicJsonType>
|
|
struct has_to_json<BasicJsonType, std_fs::path, void> : std::true_type {};
|
|
|
|
template<typename BasicJsonType>
|
|
inline void to_json(BasicJsonType& j, const std_fs::path& p)
|
|
{
|
|
// Returns either a std::string or a std::u8string depending whether library
|
|
// support for char8_t is enabled.
|
|
j = p.u8string();
|
|
}
|
|
#endif
|
|
|
|
struct to_json_fn
|
|
{
|
|
template<typename BasicJsonType, typename T>
|
|
auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward<T>(val))))
|
|
-> decltype(to_json(j, std::forward<T>(val)), void())
|
|
{
|
|
return to_json(j, std::forward<T>(val));
|
|
}
|
|
};
|
|
} // namespace detail
|
|
|
|
#ifndef JSON_HAS_CPP_17
|
|
/// namespace to hold default `to_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& to_json = // NOLINT(misc-definitions-in-headers)
|
|
detail::static_const<detail::to_json_fn>::value;
|
|
#ifndef JSON_HAS_CPP_17
|
|
} // namespace
|
|
#endif
|
|
|
|
NLOHMANN_JSON_NAMESPACE_END
|