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 =
|
static constexpr bool value =
|
||||||
not std::is_base_of<std::istream, CompatibleCompleteType>::value and
|
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
|
not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
|
||||||
has_to_json<BasicJsonType, CompatibleCompleteType>::value;
|
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 derived from `std::istream`,
|
||||||
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
||||||
constructors),
|
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.,
|
- @a CompatibleType is not a @ref basic_json nested type (e.g.,
|
||||||
@ref json_pointer, @ref iterator, etc ...)
|
@ref json_pointer, @ref iterator, etc ...)
|
||||||
- @ref @ref json_serializer<U> has a
|
- @ref @ref json_serializer<U> has a
|
||||||
|
@ -1242,6 +1243,77 @@ class basic_json
|
||||||
assert_invariant();
|
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
|
@brief create a container (array or object) from an initializer list
|
||||||
|
|
||||||
|
@ -2414,6 +2486,31 @@ class basic_json
|
||||||
return *this;
|
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)
|
@brief get a value (explicit)
|
||||||
|
|
||||||
|
@ -2455,7 +2552,7 @@ class basic_json
|
||||||
*/
|
*/
|
||||||
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
||||||
detail::enable_if_t <
|
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
|
detail::has_from_json<basic_json_t, ValueType>::value and
|
||||||
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
||||||
int> = 0>
|
int> = 0>
|
||||||
|
@ -2721,7 +2818,8 @@ class basic_json
|
||||||
template < typename ValueType, typename std::enable_if <
|
template < typename ValueType, typename std::enable_if <
|
||||||
not std::is_pointer<ValueType>::value and
|
not std::is_pointer<ValueType>::value and
|
||||||
not std::is_same<ValueType, detail::json_ref<basic_json>>::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
|
#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
|
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -466,7 +466,7 @@ struct is_compatible_complete_type
|
||||||
{
|
{
|
||||||
static constexpr bool value =
|
static constexpr bool value =
|
||||||
not std::is_base_of<std::istream, CompatibleCompleteType>::value and
|
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
|
not is_basic_json_nested_type<BasicJsonType, CompatibleCompleteType>::value and
|
||||||
has_to_json<BasicJsonType, CompatibleCompleteType>::value;
|
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 derived from `std::istream`,
|
||||||
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
- @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move
|
||||||
constructors),
|
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.,
|
- @a CompatibleType is not a @ref basic_json nested type (e.g.,
|
||||||
@ref json_pointer, @ref iterator, etc ...)
|
@ref json_pointer, @ref iterator, etc ...)
|
||||||
- @ref @ref json_serializer<U> has a
|
- @ref @ref json_serializer<U> has a
|
||||||
|
@ -10840,6 +10841,77 @@ class basic_json
|
||||||
assert_invariant();
|
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
|
@brief create a container (array or object) from an initializer list
|
||||||
|
|
||||||
|
@ -12012,6 +12084,31 @@ class basic_json
|
||||||
return *this;
|
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)
|
@brief get a value (explicit)
|
||||||
|
|
||||||
|
@ -12053,7 +12150,7 @@ class basic_json
|
||||||
*/
|
*/
|
||||||
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
template<typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
|
||||||
detail::enable_if_t <
|
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
|
detail::has_from_json<basic_json_t, ValueType>::value and
|
||||||
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
not detail::has_non_default_from_json<basic_json_t, ValueType>::value,
|
||||||
int> = 0>
|
int> = 0>
|
||||||
|
@ -12319,7 +12416,8 @@ class basic_json
|
||||||
template < typename ValueType, typename std::enable_if <
|
template < typename ValueType, typename std::enable_if <
|
||||||
not std::is_pointer<ValueType>::value and
|
not std::is_pointer<ValueType>::value and
|
||||||
not std::is_same<ValueType, detail::json_ref<basic_json>>::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
|
#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
|
and not std::is_same<ValueType, std::initializer_list<typename string_t::value_type>>::value
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -693,6 +693,83 @@ TEST_CASE("custom serializer that does adl by default", "[udt]")
|
||||||
CHECK(me == cj.get<udt::person>());
|
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
|
namespace
|
||||||
{
|
{
|
||||||
struct incomplete;
|
struct incomplete;
|
||||||
|
@ -730,6 +807,6 @@ TEST_CASE("Issue #924")
|
||||||
// Prevent get<std::vector<Evil>>() to throw
|
// Prevent get<std::vector<Evil>>() to throw
|
||||||
auto j = json::array();
|
auto j = json::array();
|
||||||
|
|
||||||
(void) j.get<Evil>();
|
CHECK_NOTHROW(j.get<Evil>());
|
||||||
(void) j.get<std::vector<Evil>>();
|
CHECK_NOTHROW(j.get<std::vector<Evil>>());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue