introduce try_get() and try_deserialize()
This commit is contained in:
parent
43ab8a2357
commit
7f4d035cfe
@ -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>
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user