Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/basic json conversion #986

Merged
merged 2 commits into from
Mar 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/nlohmann/detail/meta.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
102 changes: 100 additions & 2 deletions include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down
104 changes: 101 additions & 3 deletions single_include/nlohmann/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/src/unit-inspection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
81 changes: 79 additions & 2 deletions test/src/unit-udt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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>>());
}