Merge pull request #986 from theodelrieu/fix/basic_json_conversion
Fix/basic json conversion
This commit is contained in:
commit
62030615a0
5 changed files with 283 additions and 10 deletions
|
@ -233,7 +233,7 @@ struct is_compatible_complete_type
|
|||
{
|
||||
static constexpr bool value =
|
||||
not std::is_base_of<std::istream, CompatibleCompleteType>::value and
|
||||
not std::is_same<BasicJsonType, CompatibleCompleteType>::value and
|
||||
not is_basic_json<CompatibleCompleteType>::value and
|
||||
not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
|
||||
has_to_json<BasicJsonType, CompatibleCompleteType>::value;
|
||||
};
|
||||
|
|
|
@ -1207,6 +1207,7 @@ class basic_json
|
|||
- @a CompatibleType is not derived from `std::istream`,
|
||||
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
||||
constructors),
|
||||
- @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
|
||||
- @a CompatibleType is not a @ref basic_json nested type (e.g.,
|
||||
@ref json_pointer, @ref iterator, etc ...)
|
||||
- @ref @ref json_serializer<U> has a
|
||||
|
@ -1242,6 +1243,77 @@ class basic_json
|
|||
assert_invariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a JSON value from an existing one
|
||||
|
||||
This is a constructor for existing @ref basic_json types.
|
||||
It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones.
|
||||
|
||||
The constructor tries to convert the internal @ref m_value of the parameter.
|
||||
|
||||
@tparam BasicJsonType a type such that:
|
||||
- @a BasicJsonType is a @ref basic_json type.
|
||||
- @a BasicJsonType has different template arguments than @ref basic_json_t.
|
||||
|
||||
@param[in] val the @ref basic_json value to be converted.
|
||||
|
||||
@complexity Usually linear in the size of the passed @a val, also
|
||||
depending on the implementation of the called `to_json()`
|
||||
method.
|
||||
|
||||
@exceptionsafety Depends on the called constructor. For types directly
|
||||
supported by the library (i.e., all types for which no `to_json()` function
|
||||
was provided), strong guarantee holds: if an exception is thrown, there are
|
||||
no changes to any JSON value.
|
||||
|
||||
@since version 3.1.2
|
||||
*/
|
||||
template <typename BasicJsonType,
|
||||
detail::enable_if_t<
|
||||
detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
|
||||
basic_json(const BasicJsonType& val)
|
||||
{
|
||||
using other_boolean_t = typename BasicJsonType::boolean_t;
|
||||
using other_number_float_t = typename BasicJsonType::number_float_t;
|
||||
using other_number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using other_string_t = typename BasicJsonType::string_t;
|
||||
using other_object_t = typename BasicJsonType::object_t;
|
||||
using other_array_t = typename BasicJsonType::array_t;
|
||||
|
||||
switch (val.type())
|
||||
{
|
||||
case value_t::boolean:
|
||||
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
|
||||
break;
|
||||
case value_t::number_float:
|
||||
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
|
||||
break;
|
||||
case value_t::number_integer:
|
||||
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
|
||||
break;
|
||||
case value_t::number_unsigned:
|
||||
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&>());
|
||||
break;
|
||||
case value_t::object:
|
||||
JSONSerializer<other_object_t>::to_json(*this, 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&>());
|
||||
break;
|
||||
case value_t::null:
|
||||
*this = nullptr;
|
||||
break;
|
||||
case value_t::discarded:
|
||||
m_type = value_t::discarded;
|
||||
break;
|
||||
}
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a container (array or object) from an initializer list
|
||||
|
||||
|
@ -2414,6 +2486,31 @@ class basic_json
|
|||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief get special-case overload
|
||||
|
||||
This overloads converts the current @ref basic_json in a different @ref basic_json type
|
||||
|
||||
@tparam BasicJsonType == @ref basic_json
|
||||
|
||||
@return a copy of *this, converted into @tparam BasicJsonType
|
||||
|
||||
@complexity Depending on the implementation of the called `from_json()`
|
||||
method.
|
||||
|
||||
@since version 3.1.2
|
||||
*/
|
||||
template<typename BasicJsonType, detail::enable_if_t<
|
||||
not std::is_same<BasicJsonType, basic_json>::value and
|
||||
detail::is_basic_json<BasicJsonType>::value
|
||||
,
|
||||
int> = 0>
|
||||
BasicJsonType get() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@brief get a value (explicit)
|
||||
|
||||
|
@ -2455,7 +2552,7 @@ class basic_json
|
|||
*/
|
||||
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
||||
detail::enable_if_t <
|
||||
not std::is_same<basic_json_t, ValueType>::value and
|
||||
not detail::is_basic_json<ValueType>::value and
|
||||
detail::has_from_json<basic_json_t, ValueType>::value and
|
||||
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
||||
int> = 0>
|
||||
|
@ -2721,7 +2818,8 @@ class basic_json
|
|||
template < typename ValueType, typename std::enable_if <
|
||||
not std::is_pointer<ValueType>::value and
|
||||
not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
|
||||
not std::is_same<ValueType, typename string_t::value_type>::value
|
||||
not std::is_same<ValueType, typename string_t::value_type>::value and
|
||||
not detail::is_basic_json<ValueType>::value
|
||||
#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
|
||||
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
|
||||
#endif
|
||||
|
|
|
@ -466,7 +466,7 @@ struct is_compatible_complete_type
|
|||
{
|
||||
static constexpr bool value =
|
||||
not std::is_base_of<std::istream, CompatibleCompleteType>::value and
|
||||
not std::is_same<BasicJsonType, CompatibleCompleteType>::value and
|
||||
not is_basic_json<CompatibleCompleteType>::value and
|
||||
not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
|
||||
has_to_json<BasicJsonType, CompatibleCompleteType>::value;
|
||||
};
|
||||
|
@ -10805,6 +10805,7 @@ class basic_json
|
|||
- @a CompatibleType is not derived from `std::istream`,
|
||||
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
||||
constructors),
|
||||
- @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments)
|
||||
- @a CompatibleType is not a @ref basic_json nested type (e.g.,
|
||||
@ref json_pointer, @ref iterator, etc ...)
|
||||
- @ref @ref json_serializer<U> has a
|
||||
|
@ -10840,6 +10841,77 @@ class basic_json
|
|||
assert_invariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a JSON value from an existing one
|
||||
|
||||
This is a constructor for existing @ref basic_json types.
|
||||
It does not hijack copy/move constructors, since the parameter has different template arguments than the current ones.
|
||||
|
||||
The constructor tries to convert the internal @ref m_value of the parameter.
|
||||
|
||||
@tparam BasicJsonType a type such that:
|
||||
- @a BasicJsonType is a @ref basic_json type.
|
||||
- @a BasicJsonType has different template arguments than @ref basic_json_t.
|
||||
|
||||
@param[in] val the @ref basic_json value to be converted.
|
||||
|
||||
@complexity Usually linear in the size of the passed @a val, also
|
||||
depending on the implementation of the called `to_json()`
|
||||
method.
|
||||
|
||||
@exceptionsafety Depends on the called constructor. For types directly
|
||||
supported by the library (i.e., all types for which no `to_json()` function
|
||||
was provided), strong guarantee holds: if an exception is thrown, there are
|
||||
no changes to any JSON value.
|
||||
|
||||
@since version 3.1.2
|
||||
*/
|
||||
template <typename BasicJsonType,
|
||||
detail::enable_if_t<
|
||||
detail::is_basic_json<BasicJsonType>::value and not std::is_same<basic_json, BasicJsonType>::value, int> = 0>
|
||||
basic_json(const BasicJsonType& val)
|
||||
{
|
||||
using other_boolean_t = typename BasicJsonType::boolean_t;
|
||||
using other_number_float_t = typename BasicJsonType::number_float_t;
|
||||
using other_number_integer_t = typename BasicJsonType::number_integer_t;
|
||||
using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t;
|
||||
using other_string_t = typename BasicJsonType::string_t;
|
||||
using other_object_t = typename BasicJsonType::object_t;
|
||||
using other_array_t = typename BasicJsonType::array_t;
|
||||
|
||||
switch (val.type())
|
||||
{
|
||||
case value_t::boolean:
|
||||
JSONSerializer<other_boolean_t>::to_json(*this, val.template get<other_boolean_t>());
|
||||
break;
|
||||
case value_t::number_float:
|
||||
JSONSerializer<other_number_float_t>::to_json(*this, val.template get<other_number_float_t>());
|
||||
break;
|
||||
case value_t::number_integer:
|
||||
JSONSerializer<other_number_integer_t>::to_json(*this, val.template get<other_number_integer_t>());
|
||||
break;
|
||||
case value_t::number_unsigned:
|
||||
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&>());
|
||||
break;
|
||||
case value_t::object:
|
||||
JSONSerializer<other_object_t>::to_json(*this, 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&>());
|
||||
break;
|
||||
case value_t::null:
|
||||
*this = nullptr;
|
||||
break;
|
||||
case value_t::discarded:
|
||||
m_type = value_t::discarded;
|
||||
break;
|
||||
}
|
||||
assert_invariant();
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief create a container (array or object) from an initializer list
|
||||
|
||||
|
@ -12012,6 +12084,31 @@ class basic_json
|
|||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
@brief get special-case overload
|
||||
|
||||
This overloads converts the current @ref basic_json in a different @ref basic_json type
|
||||
|
||||
@tparam BasicJsonType == @ref basic_json
|
||||
|
||||
@return a copy of *this, converted into @tparam BasicJsonType
|
||||
|
||||
@complexity Depending on the implementation of the called `from_json()`
|
||||
method.
|
||||
|
||||
@since version 3.1.2
|
||||
*/
|
||||
template<typename BasicJsonType, detail::enable_if_t<
|
||||
not std::is_same<BasicJsonType, basic_json>::value and
|
||||
detail::is_basic_json<BasicJsonType>::value
|
||||
,
|
||||
int> = 0>
|
||||
BasicJsonType get() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
@brief get a value (explicit)
|
||||
|
||||
|
@ -12053,7 +12150,7 @@ class basic_json
|
|||
*/
|
||||
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
||||
detail::enable_if_t <
|
||||
not std::is_same<basic_json_t, ValueType>::value and
|
||||
not detail::is_basic_json<ValueType>::value and
|
||||
detail::has_from_json<basic_json_t, ValueType>::value and
|
||||
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
||||
int> = 0>
|
||||
|
@ -12319,7 +12416,8 @@ class basic_json
|
|||
template < typename ValueType, typename std::enable_if <
|
||||
not std::is_pointer<ValueType>::value and
|
||||
not std::is_same<ValueType, detail::json_ref<basic_json>>::value and
|
||||
not std::is_same<ValueType, typename string_t::value_type>::value
|
||||
not std::is_same<ValueType, typename string_t::value_type>::value and
|
||||
not detail::is_basic_json<ValueType>::value
|
||||
#ifndef _MSC_VER // fix for issue #167 operator<< ambiguity under VS2015
|
||||
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
|
||||
#endif
|
||||
|
|
|
@ -316,8 +316,8 @@ TEST_CASE("object inspection")
|
|||
SECTION("round trips")
|
||||
{
|
||||
for (const auto& s :
|
||||
{"3.141592653589793", "1000000000000000010E5"
|
||||
})
|
||||
{"3.141592653589793", "1000000000000000010E5"
|
||||
})
|
||||
{
|
||||
json j1 = json::parse(s);
|
||||
std::string s1 = j1.dump();
|
||||
|
|
|
@ -693,6 +693,83 @@ TEST_CASE("custom serializer that does adl by default", "[udt]")
|
|||
CHECK(me == cj.get<udt::person>());
|
||||
}
|
||||
|
||||
TEST_CASE("different basic_json types conversions")
|
||||
{
|
||||
using json = nlohmann::json;
|
||||
|
||||
SECTION("null")
|
||||
{
|
||||
json j;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == nullptr);
|
||||
}
|
||||
|
||||
SECTION("boolean")
|
||||
{
|
||||
json j = true;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == true);
|
||||
}
|
||||
|
||||
SECTION("discarded")
|
||||
{
|
||||
json j(json::value_t::discarded);
|
||||
custom_json cj;
|
||||
CHECK_NOTHROW(cj = j);
|
||||
CHECK(cj.type() == custom_json::value_t::discarded);
|
||||
}
|
||||
|
||||
SECTION("array")
|
||||
{
|
||||
json j = {1, 2, 3};
|
||||
custom_json cj = j;
|
||||
CHECK((cj == std::vector<int> {1, 2, 3}));
|
||||
}
|
||||
|
||||
SECTION("integer")
|
||||
{
|
||||
json j = 42;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42);
|
||||
}
|
||||
|
||||
SECTION("float")
|
||||
{
|
||||
json j = 42.0;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42.0);
|
||||
}
|
||||
|
||||
SECTION("unsigned")
|
||||
{
|
||||
json j = 42u;
|
||||
custom_json cj = j;
|
||||
CHECK(cj == 42u);
|
||||
}
|
||||
|
||||
SECTION("string")
|
||||
{
|
||||
json j = "forty-two";
|
||||
custom_json cj = j;
|
||||
CHECK(cj == "forty-two");
|
||||
}
|
||||
|
||||
SECTION("object")
|
||||
{
|
||||
json j = {{"forty", "two"}};
|
||||
custom_json cj = j;
|
||||
auto m = j.get<std::map<std::string, std::string>>();
|
||||
CHECK(cj == m);
|
||||
}
|
||||
|
||||
SECTION("get<custom_json>")
|
||||
{
|
||||
json j = 42;
|
||||
custom_json cj = j.get<custom_json>();
|
||||
CHECK(cj == 42);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct incomplete;
|
||||
|
@ -730,6 +807,6 @@ TEST_CASE("Issue #924")
|
|||
// Prevent get<std::vector<Evil>>() to throw
|
||||
auto j = json::array();
|
||||
|
||||
(void) j.get<Evil>();
|
||||
(void) j.get<std::vector<Evil>>();
|
||||
CHECK_NOTHROW(j.get<Evil>());
|
||||
CHECK_NOTHROW(j.get<std::vector<Evil>>());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue