Keep the serializer conversion for objects whose keys cannot be converted

#5591 added a test converting nlohmann::json into a basic_json whose
string type cannot be constructed from std::string. That instantiates
convert_iteratively(), whose members.emplace_back(next.key(), ...) needs
exactly that key conversion, and broke the build of unit-alt-string.
Dispatch on the key's constructibility and leave such conversions to the
serializers, as the levels above the nesting bound already do (#3425).

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
Niels Lohmann
2026-10-04 12:04:35 +02:00
parent 40021f38fb
commit 2b29ca1812
2 changed files with 60 additions and 26 deletions
+30 -13
View File
@@ -1441,7 +1441,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
be destroyed. be destroyed.
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val) void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/)
{ {
using other_const_iterator = typename BasicJsonType::const_iterator; using other_const_iterator = typename BasicJsonType::const_iterator;
@@ -1535,21 +1535,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (JSON_HEDLEY_LIKELY(guard.okay())) if (JSON_HEDLEY_LIKELY(guard.okay()))
{ {
// every element comes back to the converting constructor convert_by_serializers(val);
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
return; return;
} }
convert_iteratively(val); // the iterative conversion needs to construct this object type's keys
// from those of @a val; if it cannot, neither can the range constructor,
// and the serializers convert @a val some other way (see #3425)
convert_iteratively(val, std::is_constructible<typename object_t::key_type, const typename BasicJsonType::string_t&> {});
}
/// @brief convert the object or array @a val with the serializers; every
/// element comes back to the converting constructor
template<typename BasicJsonType>
void convert_by_serializers(const BasicJsonType& val)
{
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
}
/// @brief convert @a val whose keys cannot be converted, see @ref convert_structured
template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/)
{
convert_by_serializers(val);
} }
+30 -13
View File
@@ -27978,7 +27978,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
be destroyed. be destroyed.
*/ */
template<typename BasicJsonType> template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val) void convert_iteratively(const BasicJsonType& val, std::true_type /*unused*/)
{ {
using other_const_iterator = typename BasicJsonType::const_iterator; using other_const_iterator = typename BasicJsonType::const_iterator;
@@ -28072,21 +28072,38 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
if (JSON_HEDLEY_LIKELY(guard.okay())) if (JSON_HEDLEY_LIKELY(guard.okay()))
{ {
// every element comes back to the converting constructor convert_by_serializers(val);
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
return; return;
} }
convert_iteratively(val); // the iterative conversion needs to construct this object type's keys
// from those of @a val; if it cannot, neither can the range constructor,
// and the serializers convert @a val some other way (see #3425)
convert_iteratively(val, std::is_constructible<typename object_t::key_type, const typename BasicJsonType::string_t&> {});
}
/// @brief convert the object or array @a val with the serializers; every
/// element comes back to the converting constructor
template<typename BasicJsonType>
void convert_by_serializers(const BasicJsonType& val)
{
if (val.is_object())
{
using other_object_t = typename BasicJsonType::object_t;
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
}
else
{
using other_array_t = typename BasicJsonType::array_t;
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
}
}
/// @brief convert @a val whose keys cannot be converted, see @ref convert_structured
template<typename BasicJsonType>
void convert_iteratively(const BasicJsonType& val, std::false_type /*unused*/)
{
convert_by_serializers(val);
} }