add support for non-default-constructible udt
This commit is contained in:
parent
e5999c6c82
commit
60e6f822fa
2 changed files with 162 additions and 463 deletions
139
src/json.hpp
139
src/json.hpp
|
@ -290,14 +290,32 @@ struct is_compatible_basic_json_type
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// This trait checks if JSONSerializer<T>::from_json exists
|
// This trait checks if JSONSerializer<T>::from_json(json const&, udt&) exists
|
||||||
template <template <typename, typename> class JSONSerializer, typename Json,
|
template <template <typename, typename> class JSONSerializer, typename Json,
|
||||||
typename T>
|
typename T>
|
||||||
struct has_from_json
|
struct has_from_json
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
template <typename U, typename = decltype(uncvref_t<U>::from_json(
|
// also check the return type of from_json
|
||||||
std::declval<Json>(), std::declval<T &>()))>
|
template <typename U, typename = enable_if_t<std::is_same<void, decltype(uncvref_t<U>::from_json(
|
||||||
|
std::declval<Json>(), std::declval<T &>()))>::value>>
|
||||||
|
static int detect(U &&);
|
||||||
|
|
||||||
|
static void detect(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr bool value = std::is_integral<decltype(
|
||||||
|
detect(std::declval<JSONSerializer<T, void>>()))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This trait checks if JSONSerializer<T>::from_json(json const&) exists
|
||||||
|
// this overload is used for non-default-constructible user-defined-types
|
||||||
|
template <template <typename, typename> class JSONSerializer, typename Json,
|
||||||
|
typename T>
|
||||||
|
struct has_non_default_from_json
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template <typename U, typename = enable_if_t<std::is_same<T, decltype(uncvref_t<U>::from_json(std::declval<Json>()))>::value>>
|
||||||
static int detect(U &&);
|
static int detect(U &&);
|
||||||
|
|
||||||
static void detect(...);
|
static void detect(...);
|
||||||
|
@ -326,8 +344,8 @@ public:
|
||||||
|
|
||||||
// those declarations are needed to workaround a MSVC bug related to ADL
|
// those declarations are needed to workaround a MSVC bug related to ADL
|
||||||
// (taken from MSVC-Ranges implementation)
|
// (taken from MSVC-Ranges implementation)
|
||||||
void to_json();
|
//void to_json();
|
||||||
void from_json();
|
//void from_json();
|
||||||
|
|
||||||
struct to_json_fn
|
struct to_json_fn
|
||||||
{
|
{
|
||||||
|
@ -2467,57 +2485,63 @@ class basic_json
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
// this overload is needed, since constructor for udt is explicit
|
||||||
@brief destructor
|
template <typename T, enable_if_t<not detail::is_compatible_basic_json_type<
|
||||||
|
uncvref_t<T>, basic_json_t>::value and
|
||||||
Destroys the JSON value and frees all allocated memory.
|
detail::has_to_json<JSONSerializer, basic_json_t, uncvref_t<T>>::value>>
|
||||||
|
reference &operator=(T &&val) noexcept(std::is_nothrow_constructible<basic_json_t, uncvref_t<T>>::value and
|
||||||
@complexity Linear.
|
std::is_nothrow_move_assignable<uncvref_t<T>>::value)
|
||||||
|
|
||||||
@requirement This function helps `basic_json` satisfying the
|
|
||||||
[Container](http://en.cppreference.com/w/cpp/concept/Container)
|
|
||||||
requirements:
|
|
||||||
- The complexity is linear.
|
|
||||||
- All stored elements are destroyed and all memory is freed.
|
|
||||||
|
|
||||||
@since version 1.0.0
|
|
||||||
*/
|
|
||||||
~basic_json()
|
|
||||||
{
|
{
|
||||||
assert_invariant();
|
static_assert(sizeof(T) == 0 , "");
|
||||||
|
// I'm not sure this a is good practice...
|
||||||
|
return *this = basic_json_t{std::forward<T>(val)};
|
||||||
|
}
|
||||||
|
|
||||||
switch (m_type)
|
/*!
|
||||||
{
|
@brief destructor
|
||||||
case value_t::object:
|
|
||||||
{
|
|
||||||
AllocatorType<object_t> alloc;
|
|
||||||
alloc.destroy(m_value.object);
|
|
||||||
alloc.deallocate(m_value.object, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::array:
|
Destroys the JSON value and frees all allocated memory.
|
||||||
{
|
|
||||||
AllocatorType<array_t> alloc;
|
|
||||||
alloc.destroy(m_value.array);
|
|
||||||
alloc.deallocate(m_value.array, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case value_t::string:
|
@complexity Linear.
|
||||||
{
|
|
||||||
AllocatorType<string_t> alloc;
|
|
||||||
alloc.destroy(m_value.string);
|
|
||||||
alloc.deallocate(m_value.string, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
@requirement This function helps `basic_json` satisfying the
|
||||||
{
|
[Container](http://en.cppreference.com/w/cpp/concept/Container)
|
||||||
// all other types need no specific destructor
|
requirements:
|
||||||
break;
|
- The complexity is linear.
|
||||||
}
|
- All stored elements are destroyed and all memory is freed.
|
||||||
}
|
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
~basic_json() {
|
||||||
|
assert_invariant();
|
||||||
|
|
||||||
|
switch (m_type) {
|
||||||
|
case value_t::object: {
|
||||||
|
AllocatorType<object_t> alloc;
|
||||||
|
alloc.destroy(m_value.object);
|
||||||
|
alloc.deallocate(m_value.object, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::array: {
|
||||||
|
AllocatorType<array_t> alloc;
|
||||||
|
alloc.destroy(m_value.array);
|
||||||
|
alloc.deallocate(m_value.array, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case value_t::string: {
|
||||||
|
AllocatorType<string_t> alloc;
|
||||||
|
alloc.destroy(m_value.string);
|
||||||
|
alloc.deallocate(m_value.string, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
// all other types need no specific destructor
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -3273,6 +3297,19 @@ class basic_json
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This overload is chosen for non-default constructible user-defined-types
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
enable_if_t<not detail::is_compatible_basic_json_type<
|
||||||
|
T, basic_json_t>::value and
|
||||||
|
detail::has_non_default_from_json<JSONSerializer, basic_json_t,
|
||||||
|
T>::value,
|
||||||
|
short> = 0>
|
||||||
|
T get() const
|
||||||
|
{
|
||||||
|
return JSONSerializer<T>::from_json(*this);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@brief get a pointer value (explicit)
|
@brief get a pointer value (explicit)
|
||||||
|
|
||||||
|
|
|
@ -26,443 +26,105 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
#include "json.hpp"
|
#include "json.hpp"
|
||||||
using nlohmann::json;
|
|
||||||
|
|
||||||
namespace udt
|
namespace udt
|
||||||
{
|
{
|
||||||
struct empty_type {};
|
struct age
|
||||||
struct pod_type {
|
|
||||||
int a;
|
|
||||||
char b;
|
|
||||||
short c;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bit_more_complex_type {
|
|
||||||
pod_type a;
|
|
||||||
pod_type b;
|
|
||||||
std::string c;
|
|
||||||
};
|
|
||||||
|
|
||||||
// best optional implementation ever
|
|
||||||
template <typename T>
|
|
||||||
class optional_type
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
optional_type() = default;
|
|
||||||
explicit optional_type(T val) : _val(std::make_shared<T>(std::move(val))) {}
|
|
||||||
explicit operator bool() const noexcept { return _val != nullptr; }
|
|
||||||
|
|
||||||
T const &operator*() const { return *_val; }
|
|
||||||
optional_type& operator=(T const& t)
|
|
||||||
{
|
{
|
||||||
_val = std::make_shared<T>(t);
|
int val;
|
||||||
return *this;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
struct name
|
||||||
std::shared_ptr<T> _val;
|
{
|
||||||
};
|
std::string val;
|
||||||
|
};
|
||||||
|
|
||||||
// free to/from_json functions
|
struct address
|
||||||
|
{
|
||||||
|
std::string val;
|
||||||
|
};
|
||||||
|
|
||||||
void to_json(json& j, empty_type)
|
struct person
|
||||||
{
|
{
|
||||||
j = json::object();
|
age age;
|
||||||
|
name name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct contact
|
||||||
|
{
|
||||||
|
person person;
|
||||||
|
address address;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct contact_book
|
||||||
|
{
|
||||||
|
name book_name;
|
||||||
|
std::vector<contact> contacts;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void to_json(json& j, pod_type const& p)
|
// to_json methods for default basic_json
|
||||||
{
|
|
||||||
j = json{{"a", p.a}, {"b", p.b}, {"c", p.c}};
|
|
||||||
}
|
|
||||||
|
|
||||||
void to_json(json& j, bit_more_complex_type const& p)
|
|
||||||
{
|
|
||||||
j = json{{"a", json(p.a)}, {"b", json(p.b)}, {"c", p.c}};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void to_json(json& j, optional_type<T> const& opt)
|
|
||||||
{
|
|
||||||
if (!opt)
|
|
||||||
j = nullptr;
|
|
||||||
else
|
|
||||||
j = json(*opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(json const& j, empty_type& t)
|
|
||||||
{
|
|
||||||
assert(j.empty());
|
|
||||||
t = empty_type{};
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(json const&j, pod_type& t)
|
|
||||||
{
|
|
||||||
t = {j["a"].get<int>(), j["b"].get<char>(), j["c"].get<short>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json(json const&j, bit_more_complex_type& t)
|
|
||||||
{
|
|
||||||
// relying on json_traits struct here..
|
|
||||||
t = {j["a"].get<udt::pod_type>(), j["b"].get<udt::pod_type>(),
|
|
||||||
j["c"].get<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void from_json(json const& j, optional_type<T>& t)
|
|
||||||
{
|
|
||||||
if (j.is_null())
|
|
||||||
t = optional_type<T>{};
|
|
||||||
else
|
|
||||||
t = j.get<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(pod_type const& lhs, pod_type const& rhs) noexcept
|
|
||||||
{
|
|
||||||
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(bit_more_complex_type const &lhs,
|
|
||||||
bit_more_complex_type const &rhs) noexcept {
|
|
||||||
return std::tie(lhs.a, lhs.b, lhs.c) == std::tie(rhs.a, rhs.b, rhs.c);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline bool operator==(optional_type<T> const& lhs, optional_type<T> const& rhs)
|
|
||||||
{
|
|
||||||
if (!lhs && !rhs)
|
|
||||||
return true;
|
|
||||||
if (!lhs || !rhs)
|
|
||||||
return false;
|
|
||||||
return *lhs == *rhs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("constructors for user-defined types", "[udt]")
|
|
||||||
{
|
|
||||||
SECTION("empty type")
|
|
||||||
{
|
|
||||||
udt::empty_type const e{};
|
|
||||||
auto const j = json{e};
|
|
||||||
auto k = json::object();
|
|
||||||
CHECK(j == k);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("pod type")
|
|
||||||
{
|
|
||||||
auto const e = udt::pod_type{42, 42, 42};
|
|
||||||
auto j = json{e};
|
|
||||||
auto k = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
CHECK(j == k);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("bit more complex type")
|
|
||||||
{
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
|
|
||||||
auto j = json{e};
|
|
||||||
auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
CHECK(j == k);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("vector of udt")
|
|
||||||
{
|
|
||||||
std::vector<udt::bit_more_complex_type> v;
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
|
|
||||||
v.emplace_back(e);
|
|
||||||
v.emplace_back(e);
|
|
||||||
v.emplace_back(e);
|
|
||||||
|
|
||||||
json j = v;
|
|
||||||
auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
auto l = json{k, k, k};
|
|
||||||
CHECK(j == l);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional type") {
|
|
||||||
SECTION("regular case") {
|
|
||||||
udt::optional_type<int> u{3};
|
|
||||||
CHECK(json{u} == json(3));
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("nullopt case") {
|
|
||||||
udt::optional_type<float> v;
|
|
||||||
CHECK(json{v} == json{});
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional of json convertible type")
|
|
||||||
{
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
udt::optional_type<udt::bit_more_complex_type> o{e};
|
|
||||||
auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
CHECK(json{o} == k);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional of vector of json convertible type")
|
|
||||||
{
|
|
||||||
std::vector<udt::bit_more_complex_type> v;
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
v.emplace_back(e);
|
|
||||||
v.emplace_back(e);
|
|
||||||
v.emplace_back(e);
|
|
||||||
udt::optional_type<std::vector<udt::bit_more_complex_type>> o{v};
|
|
||||||
auto k = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
auto l = json{k, k, k};
|
|
||||||
CHECK(json{o} == l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("get<> for user-defined types", "[udt]")
|
|
||||||
{
|
|
||||||
SECTION("pod type")
|
|
||||||
{
|
|
||||||
auto const e = udt::pod_type{42, 42, 42};
|
|
||||||
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
|
|
||||||
auto const obj = j.get<udt::pod_type>();
|
|
||||||
CHECK(e == obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("bit more complex type")
|
|
||||||
{
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
auto const j = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
|
|
||||||
auto const obj = j.get<udt::bit_more_complex_type>();
|
|
||||||
CHECK(e == obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("vector of udt")
|
|
||||||
{
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
std::vector<udt::bit_more_complex_type> v{e, e, e};
|
|
||||||
auto const j = json(v);
|
|
||||||
|
|
||||||
auto const obj = j.get<decltype(v)>();
|
|
||||||
CHECK(v == obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional")
|
|
||||||
{
|
|
||||||
SECTION("from null")
|
|
||||||
{
|
|
||||||
udt::optional_type<int> o;
|
|
||||||
json j;
|
|
||||||
CHECK(j.get<decltype(o)>() == o);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("from value")
|
|
||||||
{
|
|
||||||
json j{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
auto v = j.get<udt::optional_type<udt::pod_type>>();
|
|
||||||
auto expected = udt::pod_type{42,42,42};
|
|
||||||
REQUIRE(v);
|
|
||||||
CHECK(*v == expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("to_json free function", "[udt]")
|
|
||||||
{
|
|
||||||
SECTION("pod_type")
|
|
||||||
{
|
|
||||||
auto const e = udt::pod_type{42, 42, 42};
|
|
||||||
auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
|
|
||||||
json j;
|
|
||||||
nlohmann::to_json(j, e);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("bit_more_complex_type")
|
|
||||||
{
|
|
||||||
auto const e =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
auto const expected = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
json j;
|
|
||||||
nlohmann::to_json(j, e);
|
|
||||||
CHECK(j == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional_type")
|
|
||||||
{
|
|
||||||
SECTION("from null")
|
|
||||||
{
|
|
||||||
udt::optional_type<udt::pod_type> o;
|
|
||||||
|
|
||||||
json expected;
|
|
||||||
json j;
|
|
||||||
nlohmann::to_json(j, o);
|
|
||||||
CHECK(expected == j);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("from value")
|
|
||||||
{
|
|
||||||
udt::optional_type<udt::pod_type> o{{42, 42, 42}};
|
|
||||||
|
|
||||||
auto const expected = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
json j;
|
|
||||||
nlohmann::to_json(j, o);
|
|
||||||
CHECK(expected == j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("from_json free function", "[udt]")
|
|
||||||
{
|
|
||||||
SECTION("pod_type")
|
|
||||||
{
|
|
||||||
auto const expected = udt::pod_type{42, 42, 42};
|
|
||||||
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
|
|
||||||
udt::pod_type p;
|
|
||||||
nlohmann::from_json(j, p);
|
|
||||||
CHECK(p == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("bit_more_complex_type")
|
|
||||||
{
|
|
||||||
auto const expected =
|
|
||||||
udt::bit_more_complex_type{{42, 42, 42}, {41, 41, 41}, "forty"};
|
|
||||||
auto const j = json{{"a", {{"a", 42}, {"b", 42}, {"c", 42}}},
|
|
||||||
{"b", {{"a", 41}, {"b", 41}, {"c", 41}}},
|
|
||||||
{"c", "forty"}};
|
|
||||||
udt::bit_more_complex_type p;
|
|
||||||
nlohmann::from_json(j, p);
|
|
||||||
CHECK(p == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("optional_type")
|
|
||||||
{
|
|
||||||
SECTION("from null")
|
|
||||||
{
|
|
||||||
udt::optional_type<udt::pod_type> expected;
|
|
||||||
json j;
|
|
||||||
udt::optional_type<udt::pod_type> o;
|
|
||||||
|
|
||||||
nlohmann::from_json(j, o);
|
|
||||||
CHECK(expected == o);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("from value")
|
|
||||||
{
|
|
||||||
udt::optional_type<udt::pod_type> expected{{42, 42, 42}};
|
|
||||||
auto const j = json{{"a", 42}, {"b", 42}, {"c", 42}};
|
|
||||||
udt::optional_type<udt::pod_type> o;
|
|
||||||
|
|
||||||
nlohmann::from_json(j, o);
|
|
||||||
CHECK(expected == o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// custom serializer, uses adl by default
|
|
||||||
template <typename T, typename = void>
|
|
||||||
struct my_serializer
|
|
||||||
{
|
|
||||||
template <typename Json>
|
|
||||||
static void from_json(Json const& j, T& val)
|
|
||||||
{
|
|
||||||
nlohmann::from_json(j, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Json>
|
|
||||||
static void to_json(Json& j, T const& val)
|
|
||||||
{
|
|
||||||
nlohmann::to_json(j, val);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// partial specialization on optional_type
|
|
||||||
template <typename T>
|
|
||||||
struct my_serializer<udt::optional_type<T>>
|
|
||||||
{
|
|
||||||
template <typename Json>
|
|
||||||
static void from_json(Json const& j, udt::optional_type<T>& opt)
|
|
||||||
{
|
|
||||||
if (j.is_null())
|
|
||||||
opt = nullptr;
|
|
||||||
else
|
|
||||||
opt = j.get<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Json>
|
|
||||||
static void to_json(Json& j, udt::optional_type<T> const& opt)
|
|
||||||
{
|
|
||||||
if (opt)
|
|
||||||
j = *opt;
|
|
||||||
else
|
|
||||||
j = nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using my_json = nlohmann::basic_json<std::map, std::vector, std::string, bool,
|
|
||||||
std::int64_t, std::uint64_t, double,
|
|
||||||
std::allocator, my_serializer>;
|
|
||||||
|
|
||||||
namespace udt
|
namespace udt
|
||||||
{
|
{
|
||||||
void to_json(my_json& j, pod_type const& val)
|
void to_json(nlohmann::json& j, age a)
|
||||||
{
|
{
|
||||||
j = my_json{{"a", val.a}, {"b", val.b}, {"c", val.c}};
|
j = a.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_json(my_json const& j, pod_type& val)
|
void to_json(nlohmann::json& j, name const& n)
|
||||||
{
|
{
|
||||||
val = {j["a"].get<int>(), j["b"].get<char>(), j["c"].get<short>()};
|
j = n.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& j, person const& p)
|
||||||
|
{
|
||||||
|
using nlohmann::json;
|
||||||
|
j = json{{"age", json{p.age}}, {"name", json{p.name}}};
|
||||||
|
|
||||||
|
// this unfortunately does not compile ...
|
||||||
|
// j["age"] = p.age;
|
||||||
|
// j["name"] = p.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& j, address const& a)
|
||||||
|
{
|
||||||
|
j = a.val;
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& j, contact const& c)
|
||||||
|
{
|
||||||
|
using nlohmann::json;
|
||||||
|
j = json{{"person", json{c.person}}, {"address", json{c.address}}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void to_json(nlohmann::json& j, contact_book const& cb)
|
||||||
|
{
|
||||||
|
using nlohmann::json;
|
||||||
|
j = json{{"name", json{cb.book_name}}, {"contacts", cb.contacts}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("custom serializer", "[udt]")
|
TEST_CASE("basic usage", "[udt]")
|
||||||
{
|
{
|
||||||
SECTION("default use works like default serializer")
|
using nlohmann::json;
|
||||||
|
|
||||||
|
SECTION("conversion to json via free-functions")
|
||||||
{
|
{
|
||||||
udt::pod_type pod{1, 2, 3};
|
udt::age a{23};
|
||||||
auto const j = my_json{pod};
|
|
||||||
|
|
||||||
auto const j2 = json{pod};
|
CHECK(json{a} == json{23});
|
||||||
CHECK(j.dump() == j2.dump());
|
|
||||||
|
|
||||||
auto const pod2 = j.get<udt::pod_type>();
|
// a bit narcissic maybe :) ?
|
||||||
auto const pod3 = j2.get<udt::pod_type>();
|
udt::name n{"theo"};
|
||||||
CHECK(pod2 == pod3);
|
CHECK(json{n} == json{"theo"});
|
||||||
CHECK(pod2 == pod);
|
|
||||||
|
udt::person sfinae_addict{a, n};
|
||||||
|
CHECK(json{sfinae_addict} == R"({"name":"theo", "age":23})"_json);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
SECTION("serializer specialization")
|
|
||||||
{
|
|
||||||
udt::optional_type<int> opt;
|
|
||||||
|
|
||||||
json j{opt};
|
|
||||||
CHECK(j.is_null());
|
|
||||||
|
|
||||||
opt = 42;
|
|
||||||
j = json{opt};
|
|
||||||
CHECK(j.get<udt::optional_type<int>>() == opt);
|
|
||||||
CHECK(42 == j.get<int>());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue