Skip to content

Commit

Permalink
Fix incorrect basic_json conversions
Browse files Browse the repository at this point in the history
Add wrapper types to encode conversion target value types and to_json
overloads to perform the conversions.

Fixes nlohmann#3425.
  • Loading branch information
falbrechtskirchinger committed Jun 3, 2022
1 parent 7a6e28a commit e9c8560
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 14 deletions.
92 changes: 92 additions & 0 deletions include/nlohmann/detail/conversions/to_json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ struct external_constructor<value_t::array>
template<>
struct external_constructor<value_t::object>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j)
{
j.m_value.destroy(j.m_type);
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>();
j.set_parents();
j.assert_invariant();
}

template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{
Expand Down Expand Up @@ -261,6 +271,28 @@ struct external_constructor<value_t::object>
}
};

//////////////
// wrappers //
//////////////

template<typename ArrayType>
struct array_type_wrapper
{
const ArrayType& array;
};

template<typename ObjectType>
struct object_type_wrapper
{
const ObjectType& object;
};

template<typename StringType>
struct string_type_wrapper
{
const StringType& string;
};

/////////////
// to_json //
/////////////
Expand All @@ -285,6 +317,17 @@ 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 StringType>
using string_type_constructible_from_data_and_size = decltype(typename BasicJsonType::string_t(
std::declval<const StringType&>().data(), std::declval<const StringType&>().size()));

template<typename BasicJsonType, typename StringType, detail::enable_if_t<
detail::is_detected<string_type_constructible_from_data_and_size, BasicJsonType, StringType>::value, int> = 0>
void to_json(BasicJsonType& j, detail::string_type_wrapper<StringType> s)
{
external_constructor<value_t::string>::construct(j, typename BasicJsonType::string_t(s.string.data(), s.string.size()));
}

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
Expand Down Expand Up @@ -333,6 +376,18 @@ inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
external_constructor<value_t::array>::construct(j, arr);
}

template<typename BasicJsonType, typename ArrayType>
using array_type_constructible_from_iter = decltype(typename BasicJsonType::array_t(
std::declval<result_of_begin<const ArrayType&>>(), std::declval<result_of_end<const ArrayType&>>()));

template<typename BasicJsonType, typename ArrayType, detail::enable_if_t<
detail::is_detected<array_type_constructible_from_iter, BasicJsonType, ArrayType>::value, int> = 0>
void to_json(BasicJsonType& j, detail::array_type_wrapper<ArrayType> a)
{
external_constructor<value_t::array>::construct(j,
typename BasicJsonType::array_t(a.array.begin(), a.array.end()));
}

template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
Expand Down Expand Up @@ -365,6 +420,43 @@ 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 ObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, ObjectType>::value
|| is_basic_json<ObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const object_type_wrapper<ObjectType>& obj)
{
external_constructor<value_t::object>::construct(j, obj.object);
}

template<typename BasicJsonType, typename ObjectType>
using object_type_key_constructible_from_data_and_size = decltype(
typename BasicJsonType::object_t::key_type(
std::declval<const typename ObjectType::key_type&>().data(),
std::declval<const typename ObjectType::key_type&>().size()));

template < typename BasicJsonType, typename ObjectType,
enable_if_t < !is_compatible_object_type<BasicJsonType, ObjectType>::value
&& !is_basic_json<ObjectType>::value
&& detail::is_detected<object_type_key_constructible_from_data_and_size,
BasicJsonType, ObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const object_type_wrapper<ObjectType>& o)
{
using std::begin;
using std::end;

external_constructor<value_t::object>::construct(j);

auto& obj = j.template get_ref<typename BasicJsonType::object_t&>();
std::transform(begin(o.object), end(o.object), std::inserter(obj, obj.end()),
[](const typename ObjectType::value_type & val)
{
return typename BasicJsonType::object_t::value_type
{
typename BasicJsonType::object_t::key_type(val.first.data(), val.first.size()),
BasicJsonType(val.second)};
});
}

template <
typename BasicJsonType, typename T, std::size_t N,
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
Expand Down
15 changes: 12 additions & 3 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -896,13 +896,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
JSONSerializer<other_string_t>::to_json(*this, detail::string_type_wrapper<other_string_t>
{
val.template get_ref<const other_string_t&>()
});
break;
case value_t::object:
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
JSONSerializer<other_object_t>::to_json(*this, detail::object_type_wrapper<other_object_t>
{
val.template get_ref<const other_object_t&>()
});
break;
case value_t::array:
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
JSONSerializer<other_array_t>::to_json(*this, detail::array_type_wrapper<other_array_t>
{
val.template get_ref<const other_array_t&>()
});
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
Expand Down
107 changes: 104 additions & 3 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5112,6 +5112,16 @@ struct external_constructor<value_t::array>
template<>
struct external_constructor<value_t::object>
{
template<typename BasicJsonType>
static void construct(BasicJsonType& j)
{
j.m_value.destroy(j.m_type);
j.m_type = value_t::object;
j.m_value.object = j.template create<typename BasicJsonType::object_t>();
j.set_parents();
j.assert_invariant();
}

template<typename BasicJsonType>
static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj)
{
Expand Down Expand Up @@ -5147,6 +5157,28 @@ struct external_constructor<value_t::object>
}
};

//////////////
// wrappers //
//////////////

template<typename ArrayType>
struct array_type_wrapper
{
const ArrayType& array;
};

template<typename ObjectType>
struct object_type_wrapper
{
const ObjectType& object;
};

template<typename StringType>
struct string_type_wrapper
{
const StringType& string;
};

/////////////
// to_json //
/////////////
Expand All @@ -5171,6 +5203,17 @@ 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 StringType>
using string_type_constructible_from_data_and_size = decltype(typename BasicJsonType::string_t(
std::declval<const StringType&>().data(), std::declval<const StringType&>().size()));

template<typename BasicJsonType, typename StringType, detail::enable_if_t<
detail::is_detected<string_type_constructible_from_data_and_size, BasicJsonType, StringType>::value, int> = 0>
void to_json(BasicJsonType& j, detail::string_type_wrapper<StringType> s)
{
external_constructor<value_t::string>::construct(j, typename BasicJsonType::string_t(s.string.data(), s.string.size()));
}

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
Expand Down Expand Up @@ -5219,6 +5262,18 @@ inline void to_json(BasicJsonType& j, const CompatibleArrayType& arr)
external_constructor<value_t::array>::construct(j, arr);
}

template<typename BasicJsonType, typename ArrayType>
using array_type_constructible_from_iter = decltype(typename BasicJsonType::array_t(
std::declval<result_of_begin<const ArrayType&>>(), std::declval<result_of_end<const ArrayType&>>()));

template<typename BasicJsonType, typename ArrayType, detail::enable_if_t<
detail::is_detected<array_type_constructible_from_iter, BasicJsonType, ArrayType>::value, int> = 0>
void to_json(BasicJsonType& j, detail::array_type_wrapper<ArrayType> a)
{
external_constructor<value_t::array>::construct(j,
typename BasicJsonType::array_t(a.array.begin(), a.array.end()));
}

template<typename BasicJsonType>
inline void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin)
{
Expand Down Expand Up @@ -5251,6 +5306,43 @@ 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 ObjectType,
enable_if_t < is_compatible_object_type<BasicJsonType, ObjectType>::value
|| is_basic_json<ObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const object_type_wrapper<ObjectType>& obj)
{
external_constructor<value_t::object>::construct(j, obj.object);
}

template<typename BasicJsonType, typename ObjectType>
using object_type_key_constructible_from_data_and_size = decltype(
typename BasicJsonType::object_t::key_type(
std::declval<const typename ObjectType::key_type&>().data(),
std::declval<const typename ObjectType::key_type&>().size()));

template < typename BasicJsonType, typename ObjectType,
enable_if_t < !is_compatible_object_type<BasicJsonType, ObjectType>::value
&& !is_basic_json<ObjectType>::value
&& detail::is_detected<object_type_key_constructible_from_data_and_size,
BasicJsonType, ObjectType>::value, int > = 0 >
void to_json(BasicJsonType& j, const object_type_wrapper<ObjectType>& o)
{
using std::begin;
using std::end;

external_constructor<value_t::object>::construct(j);

auto& obj = j.template get_ref<typename BasicJsonType::object_t&>();
std::transform(begin(o.object), end(o.object), std::inserter(obj, obj.end()),
[](const typename ObjectType::value_type & val)
{
return typename BasicJsonType::object_t::value_type
{
typename BasicJsonType::object_t::key_type(val.first.data(), val.first.size()),
BasicJsonType(val.second)};
});
}

template <
typename BasicJsonType, typename T, std::size_t N,
enable_if_t < !std::is_constructible<typename BasicJsonType::string_t,
Expand Down Expand Up @@ -19170,13 +19262,22 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
JSONSerializer<other_number_unsigned_t>::to_json(*this, val.template get<other_number_unsigned_t>());
break;
case value_t::string:
JSONSerializer<other_string_t>::to_json(*this, val.template get_ref<const other_string_t&>());
JSONSerializer<other_string_t>::to_json(*this, detail::string_type_wrapper<other_string_t>
{
val.template get_ref<const other_string_t&>()
});
break;
case value_t::object:
JSONSerializer<other_object_t>::to_json(*this, val.template get_ref<const other_object_t&>());
JSONSerializer<other_object_t>::to_json(*this, detail::object_type_wrapper<other_object_t>
{
val.template get_ref<const other_object_t&>()
});
break;
case value_t::array:
JSONSerializer<other_array_t>::to_json(*this, val.template get_ref<const other_array_t&>());
JSONSerializer<other_array_t>::to_json(*this, detail::array_type_wrapper<other_array_t>
{
val.template get_ref<const other_array_t&>()
});
break;
case value_t::binary:
JSONSerializer<other_binary_t>::to_json(*this, val.template get_ref<const other_binary_t&>());
Expand Down
Loading

0 comments on commit e9c8560

Please sign in to comment.