Skip to content

Commit

Permalink
feat: template spec for custom type (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
MistEO authored Feb 3, 2024
1 parent 149dc20 commit ee39af9
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 119 deletions.
45 changes: 19 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,21 @@ struct ThirdPartyStruct
int a = 100;
};
json::value to_json(const ThirdPartyStruct& t) { return t.a; }
bool check_json(const json::value& j, const ThirdPartyStruct&) { return j.is_number(); }
bool from_json(const json::value& j, ThirdPartyStruct& out) { out.a = j.as_integer(); return true; }
namespace json::ext
{
template <>
class jsonization<ThirdPartyStruct>
{
public:
json::value to_json(const ThirdPartyStruct& t) const { return t.a; }
bool check_json(const json::value& j) const { return j.is_number(); }
bool from_json(const json::value& j, ThirdPartyStruct& out) const
{
out.a = j.as_integer();
return true;
}
};
} // namespace json::ext
// 然后可以将其用作 JSON
ThirdPartyStruct third;
Expand Down Expand Up @@ -232,7 +244,6 @@ std::string format = j.dumps(4);
std::ofstream ofs("meo.json");
ofs << j;
ofs.close();
```

## 解析
Expand Down Expand Up @@ -297,43 +308,25 @@ std::string nested_get = value.get("A_obj", "B_arr", 1, "C_str", "default_value"
// 如果没有 `num`,则 opt_n 将为 std::nullopt
auto opt_n = value.find<double>("num");
if (opt_n) {
// 输出: 3.141600
// Output: 3.141600
std::cout << *opt_n << std::endl;
}

```

还有一些你在序列化中已经见过的技巧

```c++
bool is_vec = value["list"].is<std::vector<int>>();

std::vector<int> to_vec = value["list"].as_collection<int>();
to_vec = (std::vector<int>)value["list"]; // 与上面相同
to_vec = value["list"].as<std::vector<int>>(); // 与上面相同

// 输出: 1, 2, 3
// Output: 1, 2, 3
for (auto&& i : to_vec) {
std::cout << i << std::endl;
}

std::list<int> to_list = value["list"].as_collection<int, std::list>();
to_list = (std::list<int>)value["list"]; // 与上面相同
to_list = value["list"].as<std::list<int>>(); // 与上面相同

std::set<int> to_set = value["list"].as_collection<int, std::set>();
to_set = (std::set<int>)value["list"]; // 与上面相同
to_set = value["list"].as<std::set<int>>(); // 与上面相同

bool is_map = value["author"].is<std::map<std::string, std::string>>();

std::map<std::string, std::string> to_map = value["author"].as_map<std::string>();
to_map = (std::map<std::string, std::string>)value["author"]; // 与上面相同
to_map = value["author"].as<std::map<std::string, std::string>>(); // 与上面相同

to_list = (std::list<int>)value["list"]; // 和上面相同
auto to_map = value["author"].as<std::map<std::string, std::string>>();
auto to_hashmap = value["author"].as_map<std::string, std::unordered_map>();
to_hashmap = (std::unordered_map<std::string, std::string>)value["author"]; // 与上面相同
to_hashmap = value["author"].as<std::unordered_map<std::string, std::string>>();// 与上面相同
```

以及不知道有啥用的字面语法
Expand Down
39 changes: 17 additions & 22 deletions README_en.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,21 @@ struct ThirdPartyStruct
int a = 100;
};
json::value to_json(const ThirdPartyStruct& t) { return t.a; }
bool check_json(const json::value& j, const ThirdPartyStruct&) { return j.is_number(); }
bool from_json(const json::value& j, ThirdPartyStruct& out) { out.a = j.as_integer(); return true; }
namespace json::ext
{
template <>
class jsonization<ThirdPartyStruct>
{
public:
json::value to_json(const ThirdPartyStruct& t) const { return t.a; }
bool check_json(const json::value& j) const { return j.is_number(); }
bool from_json(const json::value& j, ThirdPartyStruct& out) const
{
out.a = j.as_integer();
return true;
}
};
} // namespace json::ext
// then we can use it as json
ThirdPartyStruct third;
Expand Down Expand Up @@ -305,33 +317,16 @@ There are also a few tricks you've already seen with Serializing

```c++
bool is_vec = value["list"].is<std::vector<int>>();

std::vector<int> to_vec = value["list"].as_collection<int>();
to_vec = (std::vector<int>)value["list"]; // same as above
to_vec = value["list"].as<std::vector<int>>(); // same as above

// Output: 1, 2, 3
for (auto&& i : to_vec) {
std::cout << i << std::endl;
}

std::list<int> to_list = value["list"].as_collection<int, std::list>();
to_list = (std::list<int>)value["list"]; // same as above
to_list = value["list"].as<std::list<int>>(); // same as above

std::set<int> to_set = value["list"].as_collection<int, std::set>();
to_set = (std::set<int>)value["list"]; // same as above
to_set = value["list"].as<std::set<int>>(); // same as above

bool is_map = value["author"].is<std::map<std::string, std::string>>();

std::map<std::string, std::string> to_map = value["author"].as_map<std::string>();
to_map = (std::map<std::string, std::string>)value["author"]; // same as above
to_map = value["author"].as<std::map<std::string, std::string>>(); // same as above

to_list = (std::list<int>)value["list"]; // same as above
auto to_map = value["author"].as<std::map<std::string, std::string>>();
auto to_hashmap = value["author"].as_map<std::string, std::unordered_map>();
to_hashmap = (std::unordered_map<std::string, std::string>)value["author"]; // same as above
to_hashmap = value["author"].as<std::unordered_map<std::string, std::string>>();// same as above
```

And... some useless literal syntax
Expand Down
10 changes: 6 additions & 4 deletions include/common/array.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ class basic_array
basic_array(collection_t arr)
: _array_data(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end()))
{}
template <typename jsonization_t, std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& jsonization) : basic_array(jsonization.to_json())
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& value) : basic_array(value.to_json())
{}
template <typename jsonization_t, std::enable_if_t<_utils::has_to_json_in_global<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& jsonization) : basic_array(to_json(jsonization))
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_array(const jsonization_t& value) : basic_array(ext::jsonization<jsonization_t>().to_json(value))
{}

~basic_array() noexcept = default;
Expand Down
10 changes: 6 additions & 4 deletions include/common/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ class basic_object
typename = std::enable_if_t<std::is_constructible_v<value_type, _utils::range_value_t<map_t>>>>
basic_object(map_t map) : _object_data(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end()))
{}
template <typename jsonization_t, std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& jsonization) : basic_object(jsonization.to_json())
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& value) : basic_object(value.to_json())
{}
template <typename jsonization_t, std::enable_if_t<_utils::has_to_json_in_global<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& jsonization) : basic_object(to_json(jsonization))
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_object(const jsonization_t& value) : basic_object(ext::jsonization<jsonization_t>().to_json(value))
{}

~basic_object() = default;
Expand Down
29 changes: 22 additions & 7 deletions include/common/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ using warray = basic_array<std::wstring>;
using wobject = basic_object<std::wstring>;
}

namespace json::ext
{
template <typename T>
class jsonization
{
public:
// json::value to_json(const T&) const;
// bool check_json(const json::value&) const;
// bool from_json(const json::value&, T&) const;
};
}

namespace json::_utils
{
template <typename T>
Expand Down Expand Up @@ -64,10 +76,10 @@ class has_to_json_in_member
};

template <typename T>
class has_to_json_in_global
class has_to_json_in_templ_spec
{
template <typename U>
static auto test(int) -> decltype(to_json(std::declval<U>()), std::true_type());
static auto test(int) -> decltype(std::declval<ext::jsonization<U>>().to_json(std::declval<U>()), std::true_type());

template <typename U>
static std::false_type test(...);
Expand All @@ -91,11 +103,12 @@ class has_check_json_in_member
};

template <typename T, typename string_t>
class has_check_json_in_global
class has_check_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(check_json(std::declval<json::basic_value<string_t>>(), std::declval<U>()), std::true_type());
-> decltype(std::declval<ext::jsonization<U>>().check_json(std::declval<json::basic_value<string_t>>()),
std::true_type());

template <typename U>
static std::false_type test(...);
Expand All @@ -119,11 +132,13 @@ class has_from_json_in_member
};

template <typename T, typename string_t>
class has_from_json_in_global
class has_from_json_in_templ_spec
{
template <typename U>
static auto test(int)
-> decltype(from_json(std::declval<json::basic_value<string_t>>(), std::declval<U&>()), std::true_type());
-> decltype(std::declval<ext::jsonization<U>>().from_json(std::declval<json::basic_value<string_t>>(),
std::declval<U&>()),
std::true_type());

template <typename U>
static std::false_type test(...);
Expand Down Expand Up @@ -211,4 +226,4 @@ string_t to_basic_string(any_t&& arg)
static_assert(!sizeof(any_t), "Unsupported type");
}
}
} // namespace json::utils
} // namespace json::_utils
14 changes: 7 additions & 7 deletions include/common/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ class basic_value

template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_member<jsonization_t>::value, bool> = true>
basic_value(const jsonization_t& jsonization) : basic_value(jsonization.to_json())
basic_value(const jsonization_t& value) : basic_value(value.to_json())
{}
template <typename jsonization_t,
std::enable_if_t<_utils::has_to_json_in_global<jsonization_t>::value, bool> = true>
basic_value(const jsonization_t& jsonization) : basic_value(to_json(jsonization))
std::enable_if_t<_utils::has_to_json_in_templ_spec<jsonization_t>::value, bool> = true>
basic_value(const jsonization_t& value) : basic_value(ext::jsonization<jsonization_t>().to_json(value))
{}

template <typename value_t, std::enable_if_t<!std::is_convertible_v<value_t, basic_value<string_t>>, bool> = true>
Expand Down Expand Up @@ -230,11 +230,11 @@ class basic_value
return dst;
}
template <typename jsonization_t,
std::enable_if_t<_utils::has_from_json_in_global<jsonization_t, string_t>::value, bool> = true>
std::enable_if_t<_utils::has_from_json_in_templ_spec<jsonization_t, string_t>::value, bool> = true>
explicit operator jsonization_t() const
{
jsonization_t dst;
if (!from_json(*this, dst)) {
if (!ext::jsonization<jsonization_t>().from_json(*this, dst)) {
throw exception("Wrong JSON");
}
return dst;
Expand Down Expand Up @@ -389,8 +389,8 @@ inline bool basic_value<string_t>::is() const noexcept
else if constexpr (_utils::has_check_json_in_member<value_t, string_t>::value) {
return value_t().check_json(*this);
}
else if constexpr (_utils::has_check_json_in_global<value_t, string_t>::value) {
return check_json(*this, value_t());
else if constexpr (_utils::has_check_json_in_templ_spec<value_t, string_t>::value) {
return ext::jsonization<value_t>().check_json(*this);
}
else {
static_assert(!sizeof(value_t), "Unsupported type");
Expand Down
45 changes: 15 additions & 30 deletions sample/sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,19 +167,21 @@ struct ThirdPartyStruct
int a = 100;
};

json::value to_json(const ThirdPartyStruct& t)
namespace json::ext
{
return t.a;
}
bool check_json(const json::value& j, const ThirdPartyStruct&)
{
return j.is_number();
}
bool from_json(const json::value& j, ThirdPartyStruct& out)
template <>
class jsonization<ThirdPartyStruct>
{
out.a = j.as_integer();
return true;
}
public:
json::value to_json(const ThirdPartyStruct& t) const { return t.a; }
bool check_json(const json::value& j) const { return j.is_number(); }
bool from_json(const json::value& j, ThirdPartyStruct& out) const
{
out.a = j.as_integer();
return true;
}
};
} // namespace json::ext

void third_party_jsonization_1()
{
Expand Down Expand Up @@ -295,33 +297,16 @@ void parsing()
/* There are also a few tricks you've already seen with Serializing */

bool is_vec = value["list"].is<std::vector<int>>();

std::vector<int> to_vec = value["list"].as_collection<int>();
to_vec = (std::vector<int>)value["list"]; // same as above
to_vec = value["list"].as<std::vector<int>>(); // same as above

// Output: 1, 2, 3
for (auto&& i : to_vec) {
std::cout << i << std::endl;
}

std::list<int> to_list = value["list"].as_collection<int, std::list>();
to_list = (std::list<int>)value["list"]; // same as above
to_list = value["list"].as<std::list<int>>(); // same as above

std::set<int> to_set = value["list"].as_collection<int, std::set>();
to_set = (std::set<int>)value["list"]; // same as above
to_set = value["list"].as<std::set<int>>(); // same as above

bool is_map = value["author"].is<std::map<std::string, std::string>>();

std::map<std::string, std::string> to_map = value["author"].as_map<std::string>();
to_map = (std::map<std::string, std::string>)value["author"]; // same as above
to_map = value["author"].as<std::map<std::string, std::string>>(); // same as above

to_list = (std::list<int>)value["list"]; // same as above
auto to_map = value["author"].as<std::map<std::string, std::string>>();
auto to_hashmap = value["author"].as_map<std::string, std::unordered_map>();
to_hashmap = (std::unordered_map<std::string, std::string>)value["author"]; // same as above
to_hashmap = value["author"].as<std::unordered_map<std::string, std::string>>(); // same as above

/* And... some useless literal syntax */

Expand Down
Loading

0 comments on commit ee39af9

Please sign in to comment.