introduce try_get() and try_deserialize()

This commit is contained in:
oficsu 2020-07-14 15:45:21 +03:00
parent 43ab8a2357
commit 7f4d035cfe
4 changed files with 197 additions and 0 deletions

View File

@ -87,6 +87,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template<typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
template<typename T, typename... Args>
using try_deserialize_function = decltype(T::try_deserialize(std::declval<Args>()...));
template<typename T, typename U>
using get_template_function = decltype(std::declval<T>().template get<U>());
@ -120,6 +123,20 @@ struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_jso
const BasicJsonType&>::value;
};
template<typename BasicJsonType, typename T, typename = void>
struct has_try_deserialize : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_try_deserialize < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected<try_deserialize_function, serializer,
const BasicJsonType&>::value;
};
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
template<typename BasicJsonType, typename T, typename = void>

View File

@ -2977,6 +2977,38 @@ class basic_json
return JSONSerializer<ValueType>::from_json(*this);
}
/*!
@brief get a wrapped value (explicit)
Explicit type conversion between the JSON value and a compatible value wrapper
The value is converted by calling the @ref json_serializer<ValueType>
`try_deserialize()` method.
The function is equivalent to executing
@code {.cpp}
return JSONSerializer<ValueTypeCV>::try_deserialize(*this);
@endcode
@tparam ValueTypeCV the provided value type
@tparam ValueType the returned value type
@return copy of the JSON value, converted to @a ValueType wrapper
@throw what @ref json_serializer<ValueType> `try_deserialize()` method throws
*/
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
typename ReturnType = decltype(JSONSerializer<ValueType>::try_deserialize(std::declval<const basic_json_t&>())),
detail::enable_if_t < !std::is_same<basic_json_t, ValueType>::value &&
detail::has_try_deserialize<basic_json_t, ValueType>::value,
int > = 0 >
ReturnType try_get() const noexcept(noexcept(
JSONSerializer<ValueType>::try_deserialize(std::declval<const basic_json_t&>())))
{
static_assert(!std::is_reference<ValueTypeCV>::value,
"get() cannot be used with reference types, you might want to use get_ref()");
return JSONSerializer<ValueType>::try_deserialize(*this);
}
/*!
@brief get a value (explicit)

View File

@ -2902,6 +2902,9 @@ using to_json_function = decltype(T::to_json(std::declval<Args>()...));
template<typename T, typename... Args>
using from_json_function = decltype(T::from_json(std::declval<Args>()...));
template<typename T, typename... Args>
using try_deserialize_function = decltype(T::try_deserialize(std::declval<Args>()...));
template<typename T, typename U>
using get_template_function = decltype(std::declval<T>().template get<U>());
@ -2935,6 +2938,20 @@ struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_jso
const BasicJsonType&>::value;
};
template<typename BasicJsonType, typename T, typename = void>
struct has_try_deserialize : std::false_type {};
template<typename BasicJsonType, typename T>
struct has_try_deserialize < BasicJsonType, T,
enable_if_t < !is_basic_json<T>::value >>
{
using serializer = typename BasicJsonType::template json_serializer<T, void>;
static constexpr bool value =
is_detected<try_deserialize_function, serializer,
const BasicJsonType&>::value;
};
// This trait checks if BasicJsonType::json_serializer<T>::to_json exists
// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion.
template<typename BasicJsonType, typename T, typename = void>
@ -19134,6 +19151,38 @@ class basic_json
return JSONSerializer<ValueType>::from_json(*this);
}
/*!
@brief get a wrapped value (explicit)
Explicit type conversion between the JSON value and a compatible value wrapper
The value is converted by calling the @ref json_serializer<ValueType>
`try_deserialize()` method.
The function is equivalent to executing
@code {.cpp}
return JSONSerializer<ValueTypeCV>::try_deserialize(*this);
@endcode
@tparam ValueTypeCV the provided value type
@tparam ValueType the returned value type
@return copy of the JSON value, converted to @a ValueType wrapper
@throw what @ref json_serializer<ValueType> `try_deserialize()` method throws
*/
template < typename ValueTypeCV, typename ValueType = detail::uncvref_t<ValueTypeCV>,
typename ReturnType = decltype(JSONSerializer<ValueType>::try_deserialize(std::declval<const basic_json_t&>())),
detail::enable_if_t < !std::is_same<basic_json_t, ValueType>::value &&
detail::has_try_deserialize<basic_json_t, ValueType>::value,
int > = 0 >
ReturnType try_get() const noexcept(noexcept(
JSONSerializer<ValueType>::try_deserialize(std::declval<const basic_json_t&>())))
{
static_assert(!std::is_reference<ValueTypeCV>::value,
"get() cannot be used with reference types, you might want to use get_ref()");
return JSONSerializer<ValueType>::try_deserialize(*this);
}
/*!
@brief get a value (explicit)

View File

@ -541,6 +541,105 @@ TEST_CASE("Non-copyable types" * doctest::test_suite("udt"))
}
}
namespace udt
{
struct book
{
std::shared_ptr<person> m_author;
std::string m_content;
book(std::shared_ptr<person> a, const std::string& c) : m_author(a), m_content(c) {}
};
// operators
static bool operator==(const std::shared_ptr<person>& lhs, const std::shared_ptr<person>& rhs)
{
if (!lhs && !rhs)
{
return true;
}
if (!lhs || ! rhs)
{
return false;
}
return *lhs == *rhs;
}
static bool operator==(const book& lhs, const book& rhs)
{
return std::tie(lhs.m_author, lhs.m_content) ==
std::tie(rhs.m_author, rhs.m_content);
}
}
namespace nlohmann
{
template <>
struct adl_serializer<udt::book>
{
static void to_json(json& j, const udt::book& opt)
{
j["author"] = opt.m_author;
j["content"] = opt.m_content;
}
// this is the overload needed for non-copyable types,
static std::unique_ptr<udt::book> try_deserialize(const json& j)
{
if (j.is_null())
{
return nullptr;
}
else
{
auto author = j["author"].get<std::shared_ptr<udt::person>>();
if (!j.contains("content") || j["content"].is_null())
{
return nullptr;
}
auto content = j["content"].get<std::string>();
return std::unique_ptr<udt::book>(new udt::book(author, content));
}
}
};
}
TEST_CASE("try_deserialize" * doctest::test_suite("udt"))
{
SECTION("from valid object")
{
auto person = udt::person{{42}, {"John Doe"}, udt::country::russia};
auto content = std::string{"Lorem ipsum dolor sit amet, consectetur adipiscing elit."};
udt::book book{std::shared_ptr<udt::person>(new udt::person(person)), content};
json j = book;
auto optBook = j.try_get<udt::book>();
REQUIRE(optBook);
CHECK(*optBook == book);
}
SECTION("from null")
{
json j = nullptr;
auto optBook = j.try_get<udt::book>();
REQUIRE(!optBook);
}
SECTION("from invalid object")
{
json j = R"({"author" : {"age":23, "name":"theo", "country":"France"}})"_json;
auto optBook = j.try_get<udt::book>();
REQUIRE(!optBook);
}
}
// custom serializer - advanced usage
// pack structs that are pod-types (but not scalar types)
// relies on adl for any other type