yalantinglibs/include/ylt/standalone/iguana/json_writer.hpp

285 lines
9.0 KiB
C++

//
// Created by Qiyu on 17-6-5.
//
#ifndef SERIALIZE_JSON_HPP
#define SERIALIZE_JSON_HPP
#include "json_util.hpp"
namespace iguana {
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s);
namespace detail {
template <bool Is_writing_escape = true, typename Stream, typename T>
IGUANA_INLINE void to_json_impl(Stream &ss, std::optional<T> &val);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t);
template <typename Stream, typename InputIt, typename T, typename F>
IGUANA_INLINE void join(Stream &ss, InputIt first, InputIt last, const T &delim,
const F &f) {
if (first == last)
return;
f(*first++);
while (first != last) {
ss.push_back(delim);
f(*first++);
}
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, std::nullptr_t) {
ss.append("null");
}
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, bool b) {
ss.append(b ? "true" : "false");
};
template <bool Is_writing_escape, typename Stream>
IGUANA_INLINE void to_json_impl(Stream &ss, char value) {
ss.append("\"");
ss.push_back(value);
ss.append("\"");
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T value) {
char temp[65];
auto p = detail::to_chars(temp, value);
ss.append(temp, p - temp);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<numeric_str_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T v) {
ss.append(v.value().data(), v.value().size());
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
ss.push_back('"');
if constexpr (Is_writing_escape) {
write_string_with_escape(t.data(), t.size(), ss);
}
else {
ss.append(t.data(), t.size());
}
ss.push_back('"');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<num_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &t) {
ss.push_back('"');
to_json_impl<Is_writing_escape>(ss, t);
ss.push_back('"');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<string_container_v<T>, int> = 0>
IGUANA_INLINE void render_key(Stream &ss, T &&t) {
to_json_impl<Is_writing_escape>(ss, std::forward<T>(t));
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T &&t) {
to_json(std::forward<T>(t), ss);
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<enum_v<T>, int> = 0>
IGUANA_INLINE void to_json_impl(Stream &ss, T val) {
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
to_json_impl<Is_writing_escape>(
ss, static_cast<std::underlying_type_t<T>>(val));
}
else {
auto it = enum_to_str.find(val);
if (it != enum_to_str.end())
IGUANA_LIKELY {
auto str = it->second;
to_json_impl<Is_writing_escape>(
ss, std::string_view(str.data(), str.size()));
}
else {
throw std::runtime_error(
std::to_string(static_cast<std::underlying_type_t<T>>(val)) +
" is a missing value in enum_value");
}
}
}
template <bool Is_writing_escape, typename Stream, typename T>
IGUANA_INLINE void to_json_impl(Stream &ss, std::optional<T> &val) {
if (!val) {
ss.append("null");
}
else {
to_json_impl<Is_writing_escape>(ss, *val);
}
}
template <bool Is_writing_escape, typename Stream, typename T>
IGUANA_INLINE void render_array(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, std::begin(v), std::end(v), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<fixed_array_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &t) {
if constexpr (std::is_same_v<char, std::remove_reference_t<
decltype(std::declval<T>()[0])>>) {
constexpr size_t n = sizeof(T) / sizeof(decltype(std::declval<T>()[0]));
ss.push_back('"');
auto get_length = [&t](int n) constexpr {
for (int i = 0; i < n; ++i) {
if (t[i] == '\0')
return i;
}
return n;
};
size_t len = get_length(n);
ss.append(std::begin(t), len);
ss.push_back('"');
}
else {
render_array<Is_writing_escape>(ss, t);
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<map_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &o) {
ss.push_back('{');
join(ss, o.cbegin(), o.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
render_key<Is_writing_escape>(ss, jsv.first);
ss.push_back(':');
to_json_impl<Is_writing_escape>(ss, jsv.second);
});
ss.push_back('}');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<sequence_container_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
ss.push_back('[');
join(ss, v.cbegin(), v.cend(), ',',
[&ss](const auto &jsv) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(ss, jsv);
});
ss.push_back(']');
}
constexpr auto write_json_key = [](auto &s, auto i,
auto &t) IGUANA__INLINE_LAMBDA {
s.push_back('"');
// will be replaced by string_view later
constexpr auto name = get_name<decltype(t), decltype(i)::value>();
s.append(name.data(), name.size());
s.push_back('"');
};
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<smart_ptr_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &ss, const T &v) {
if (v) {
to_json_impl<Is_writing_escape>(ss, *v);
}
else {
ss.append("null");
}
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<tuple_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
using U = typename std::decay_t<T>;
s.push_back('[');
constexpr size_t size = std::tuple_size_v<U>;
for_each(std::forward<T>(t),
[&s, size](auto &v, auto i) IGUANA__INLINE_LAMBDA {
to_json_impl<Is_writing_escape>(s, v);
if (i != size - 1)
IGUANA_LIKELY { s.push_back(','); }
});
s.push_back(']');
}
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<variant_v<T>, int>>
IGUANA_INLINE void to_json_impl(Stream &s, T &&t) {
std::visit(
[&s](auto value) {
to_json_impl<Is_writing_escape>(s, value);
},
t);
}
} // namespace detail
template <bool Is_writing_escape, typename Stream, typename T,
std::enable_if_t<refletable_v<T>, int>>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
s.push_back('{');
for_each(std::forward<T>(t),
[&t, &s](const auto &v, auto i) IGUANA__INLINE_LAMBDA {
using M = decltype(iguana_reflect_type(std::forward<T>(t)));
constexpr auto Idx = decltype(i)::value;
constexpr auto Count = M::value();
static_assert(Idx < Count);
write_json_key(s, i, t);
s.push_back(':');
to_json_impl<Is_writing_escape>(s, t.*v);
if (Idx < Count - 1)
IGUANA_LIKELY { s.push_back(','); }
});
s.push_back('}');
}
template <bool Is_writing_escape = true, typename Stream, typename T,
std::enable_if_t<non_refletable_v<T>, int> = 0>
IGUANA_INLINE void to_json(T &&t, Stream &s) {
using namespace detail;
to_json_impl<Is_writing_escape>(s, t);
}
} // namespace iguana
#endif // SERIALIZE_JSON_HPP