Files
CPP_Core/Core/Base/JSON.h
T
2026-06-29 09:40:55 +08:00

543 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include "../Base/global_include.h"
#include "../system/export.h"
#include <atomic>
#include <functional>
#include <iostream>
#include <limits> // std::numeric_limits
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#define PSC_JSON_KEY_PARAMS(M, SEP) M(std::string_view, key)
#define PSC_JSON_PARSE_PARAMS(M, SEP) M(std::string_view, text)
#define PSC_JSON_FILE_PARAMS(M, SEP) M(std::string_view, file_name)
#undef max
#undef min
#define Json_GET(type, name) \
[[nodiscard]] type get_##name(std::string_view _key) const { \
const Psc::JSON *that = get(_key); \
if (!that) { \
std::cout << "get" #type " error json not found! key:" << _key \
<< " json: " << to_json_string() << std::endl; \
return false; \
} \
return that->name##_val(); \
}
#pragma once
#include "../Base/global_include.h"
#include <stdexcept>
#include <string>
#include <system_error>
#include <utility>
namespace Psc {
class json_assign_error final : public std::system_error {
public:
using std::system_error::system_error;
};
enum JsonType : uint8_t { String, Object, Array, Null, Number, Bool };
template <typename T>
void assign_json(JsonType& rt, std::string& rv, T&& val);
template <typename T_Number>
bool to_number_ec(std::string_view val, T_Number& out,
std::error_code& ec) noexcept;
template <typename T_Number>
T_Number to_number(std::string_view val) {
return detail::triple_api_throwing<T_Number>(
[&val](T_Number& out, std::error_code& ec) {
return to_number_ec(val, out, ec);
},
"to_number failed");
}
template <typename T_Number>
expected<T_Number, std::error_code> try_to_number(std::string_view val) noexcept {
return detail::triple_api_expected<T_Number>(
[&val](T_Number& out, std::error_code& ec) {
return to_number_ec(val, out, ec);
});
}
template <typename T_Number>
bool to_number_ec(std::string_view val, T_Number& out,
std::error_code& ec) noexcept {
ec.clear();
// 限定只支持整数/浮点,其他类型编译期报错(比运行时返回默认值更安全)
static_assert(std::is_integral_v<T_Number> ||
std::is_floating_point_v<T_Number>,
"to_number<T_Number>: T_Number must be an integral or "
"floating-point type");
try {
if constexpr (std::is_integral_v<T_Number>) {
if constexpr (std::is_unsigned_v<T_Number>) {
// stoull: 负号、非数字等会抛 invalid_argument;超范围抛 out_of_range
auto text = std::string(val);
unsigned long long x = std::stoull(text);
// 额外范围检查,避免静默截断
if (x > static_cast<unsigned long long>(
std::numeric_limits<T_Number>::max())) {
ec = std::make_error_code(std::errc::result_out_of_range);
return false;
}
out = static_cast<T_Number>(x);
return true;
}
else {
auto text = std::string(val);
long long x = std::stoll(text);
// 额外范围检查,避免静默截断
if (x < static_cast<long long>(std::numeric_limits<T_Number>::min()) ||
x > static_cast<long long>(std::numeric_limits<T_Number>::max())) {
ec = std::make_error_code(std::errc::result_out_of_range);
return false;
}
out = static_cast<T_Number>(x);
return true;
}
}
else {
// floating point
auto text = std::string(val);
long double x = std::stold(text);
// 可选:检查是否超出目标浮点类型范围
if (x < -static_cast<long double>(std::numeric_limits<T_Number>::max()) ||
x > static_cast<long double>(std::numeric_limits<T_Number>::max())) {
ec = std::make_error_code(std::errc::result_out_of_range);
return false;
}
out = static_cast<T_Number>(x);
return true;
}
}
catch (const std::invalid_argument&) {
ec = std::make_error_code(std::errc::invalid_argument);
return false; // 不能解析为数字
}
catch (const std::out_of_range&) {
ec = std::make_error_code(std::errc::result_out_of_range);
return false; // 数值超范围
}
}
class JSON {
public:
static std::function<void(const char *, std::string_view, const char *,
int)>
handle_error;
explicit operator std::string() const;
friend std::ostream& operator<<(std::ostream& os, const JSON& obj);
[[nodiscard]] bool has(std::string_view key) const;
[[nodiscard]] const JSON* get(std::string_view key) const;
PSC_DECLARE_TRIPLE_API_CONST_METHOD(std::string, get_string,
PSC_JSON_KEY_PARAMS);
PSC_DECLARE_TRIPLE_API_CONST_METHOD_0(bool, bool_val);
PSC_DECLARE_TRIPLE_API_CONST_METHOD(bool, get_bool, PSC_JSON_KEY_PARAMS);
template <typename T_Number>
T_Number get_number(std::string_view key) const {
return detail::triple_api_throwing<T_Number>(
[this, &key](T_Number& out, std::error_code& ec) {
return get_number_ec(key, out, ec);
},
"get_number failed");
}
template <typename T_Number>
expected<T_Number, std::error_code> try_get_number(std::string_view key) const noexcept {
return detail::triple_api_expected<T_Number>(
[this, &key](T_Number& out, std::error_code& ec) {
return get_number_ec(key, out, ec);
});
}
template <typename T_Number>
bool get_number_ec(std::string_view key, T_Number& out,
std::error_code& ec) const noexcept {
const JSON *that = get(key);
if (!that) {
ec = std::make_error_code(std::errc::invalid_argument);
return false;
}
return that->number_val_ec(out, ec);
}
template <typename T_Number>
T_Number number_val() const {
return detail::triple_api_throwing<T_Number>(
[this](T_Number& out, std::error_code& ec) {
return number_val_ec(out, ec);
},
"number_val failed");
}
template <typename T_Number>
expected<T_Number, std::error_code> try_number_val() const noexcept {
return detail::triple_api_expected<T_Number>(
[this](T_Number& out, std::error_code& ec) {
return number_val_ec(out, ec);
});
}
template <typename T_Number>
bool number_val_ec(T_Number& out, std::error_code& ec) const noexcept {
if (valueType != Number) {
ec = std::make_error_code(std::errc::invalid_argument);
return false;
}
return to_number_ec(val, out, ec);
}
JSON(std::string_view key, const JSON& body);
template <typename T,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, JSON>>>
JSON(std::string_view k, T&& v) {
key = std::string(k);
assign_json(valueType, val, std::forward<T>(v));
}
template <typename T,
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, JSON>>>
JSON(T&& v) {
assign_json(valueType, val, std::forward<T>(v));
}
JSON();
JSON(const JSON& other);
JSON(JSON&& other) noexcept;
JSON& operator=(const JSON& other);
JSON& operator=(JSON&& other) noexcept;
JSON& operator+=(const JSON& child);
JSON& operator+=(JSON&& child);
void append_list(const std::vector<JSON>& arr);
void append(const JSON& json);
void prepend(const JSON& json);
static JSON array(std::string_view key, const std::vector<JSON>& arr);
static JSON object(std::string_view key, const std::vector<JSON>& children);
static JSON array(const std::vector<JSON>& arr = {});
static JSON object(const std::vector<JSON>& children = {});
JsonType valueType = Null;
std::string key, val;
std::vector<JSON> children;
[[nodiscard]] std::string to_json_string(std::string_view object_split = "\n",
std::string_view array_split = "\n",
std::string_view indentStr = " ",
const int& indent = 0) const;
private:
static std::string get_indent(const int& indent,
std::string_view indentStr);
void append_value_string(std::string& ret, const int& indent, bool isEnd,
std::string_view object_split,
std::string_view array_split,
std::string_view indentStr,
JsonType fatherType) const;
};
PSC_DECLARE_TRIPLE_API(JSON, parse_json, PSC_JSON_PARSE_PARAMS);
PSC_DECLARE_TRIPLE_API(JSON, parse_json_file, PSC_JSON_FILE_PARAMS);
template <typename T>
void assign_json(JsonType& rt, std::string& rv, T&& v) {
using C = std::decay_t<T>;
if constexpr (std::is_same_v<C, std::string>) {
rt = String;
rv = std::forward<T>(v);
}
else if constexpr (std::is_same_v<C, std::string_view>) {
rt = String;
rv = std::string(v);
}
else if constexpr (std::is_same_v<C, void*>) {
std::stringstream ss;
ss << "0x" << std::setw(sizeof(void*) * 2) << std::setfill('0') << std::hex
<< reinterpret_cast<std::uintptr_t>(v);
rt = String;
rv = ss.str();
}
// 处理 char* 类型
else if constexpr (std::is_same_v<C, char*>) {
rt = String;
rv = std::string(v);
}
else if constexpr (std::is_same_v<C, const char*>) {
rt = String;
rv = std::string(v); // 或 val = v;
}
// 处理 wchar_t* 类型
else if constexpr (std::is_same_v<C, wchar_t*>) {
std::wstring ws(v);
rt = String;
rv = std::string(ws.begin(), ws.end());
}
// 处理 std::wstring 类型
else if constexpr (std::is_same_v<C, std::wstring>) {
rt = String;
rv = std::string(v.begin(), v.end());
}
// 处理枚举类型
else if constexpr (std::is_enum_v<C>) {
rt = String;
rv = std::string(magic_enum::enum_name(v));
}
// 处理 bool 类型
else if constexpr (std::is_same_v<C, bool>) {
rt = Bool;
rv = v ? "true" : "false"; // 对 bool 类型进行自定义转换
}
// 处理 std::nullptr_t 类型
else if constexpr (std::is_same_v<C, std::nullptr_t>) {
rt = Null;
rv = "null";
}
else if constexpr (std::is_arithmetic_v<C>) {
rt = Number;
rv = std::to_string(v);
}
else if constexpr (is_atomic_v<C> || is_copyable_atomic_v<C>) {
assign_json(rt, rv, v.load());
}
else if constexpr (is_optional_v<C>) {
if (v.has_value()) {
assign_json(rt, rv, v.value());
}
else {
rt = Null;
rv = "null";
}
}
else {
rt = String;
rv = v.to_string();
}
}
void check_json_assign_field(bool ok, const std::error_code& ec,
const Psc::JSON *that_json, const char *name);
template <typename T>
bool assign_field_ec(T& field, const Psc::JSON *that_json, const char *name, std::error_code& ec) noexcept;
template <typename T>
void assign_field(T& field, const Psc::JSON *that_json, const char *name) {
detail::triple_api_throwing_void(
[&field, that_json, name](std::error_code& ec) {
return assign_field_ec(field, that_json, name, ec);
},
"assign_field failed");
}
template <typename T>
expected<void, std::error_code> try_assign_field(T& field,
const Psc::JSON *that_json,
const char *name) noexcept {
return detail::triple_api_expected_void(
[&field, that_json, name](std::error_code& ec) {
return assign_field_ec(field, that_json, name, ec);
});
}
template <typename T>
bool assign_field_ec(T& field, const Psc::JSON *that_json, const char *name,
std::error_code& ec) noexcept {
using C = clean_t<T>;
if (that_json == nullptr) {
ec = std::make_error_code(std::errc::invalid_argument);
return false;
}
auto j = that_json->get(name);
if (j == nullptr) {
ec = std::make_error_code(std::errc::invalid_argument);
return false;
}
if constexpr (std::is_same_v<C, bool>) {
return j->bool_val_ec(field, ec);
}
else if constexpr (std::is_arithmetic_v<C> && !std::is_same_v<C, bool>) {
return that_json->get_number_ec(name, field, ec);
}
else if constexpr (std::is_same_v<C, std::string>) {
return that_json->get_string_ec(name, field, ec);
}
else if constexpr (std::is_enum_v<C>) {
std::string value;
if (!that_json->get_string_ec(name, value, ec)) return false;
field = Psc::to_enum<C>(value);
}
else if constexpr (is_atomic_v<T>) {
using V = atomic_value_t<T>;
V value{};
if constexpr (std::is_same_v<V, bool>) {
if (!that_json->get_bool_ec(name, value, ec)) return false;
}
else if constexpr (std::is_arithmetic_v<V>) {
if (!that_json->get_number_ec(name, value, ec)) return false;
}
field = value;
}
else if constexpr (is_copyable_atomic_v<T>) {
using V = copyable_atomic_value_t<T>;
V value{};
if constexpr (std::is_same_v<V, bool>) {
if (!that_json->get_bool_ec(name, value, ec)) {
return false;
}
}
else if constexpr (std::is_arithmetic_v<V>) {
if (!that_json->get_number_ec(name, value, ec)) {
return false;
}
}
field = value;
}
else if constexpr (is_optional_v<T>) {
using V = optional_value_t<T>;
V value{};
if constexpr (std::is_same_v<V, bool>) {
if (!that_json->get_bool_ec(name, value, ec)) return false;
}
else if constexpr (std::is_arithmetic_v<V> && !std::is_same_v<V, bool>) {
if (!that_json->get_number_ec(name, value, ec)) return false;
}
else if constexpr (std::is_same_v<V, std::string>) {
if (!that_json->get_string_ec(name, value, ec)) return false;
}
else if constexpr (std::is_enum_v<V>) {
std::string text;
if (!that_json->get_string_ec(name, text, ec)) return false;
value = Psc::to_enum<V>(text);
}
else {
static_assert(!sizeof(T),
"assign_field does not support this optional type");
}
field = std::move(value);
}
else {
static_assert(!sizeof(T), "assign_field does not support this type");
}
return true;
}
template <typename T>
void output_field(const T& field, Psc::JSON& ret, const char *name) {
using C = clean_t<T>;
// bool
if constexpr (std::is_same_v<C, bool>) {
ret.append(Psc::JSON(name, field));
}
// 数字(排除 bool
else if constexpr (std::is_arithmetic_v<C> && !std::is_same_v<C, bool>) {
ret.append(Psc::JSON(name, field));
}
// string
else if constexpr (std::is_same_v<C, std::string>) {
ret.append(Psc::JSON(name, field));
}
// enum
else if constexpr (std::is_enum_v<C>) {
ret.append(Psc::JSON(name, Psc::to_string(field)));
}
// atomic<T>
else if constexpr (is_atomic_v<T> || is_copyable_atomic_v<C>) {
ret.append(Psc::JSON(name, field.load()));
}
// optional<T>
else if constexpr (is_optional_v<T>) {
using V = optional_value_t<T>;
if (field.has_value()) {
// --- optional<bool>
if constexpr (std::is_same_v<V, bool>) {
ret.append(Psc::JSON(name, field.value()));
}
// --- optional<number>
else if constexpr (std::is_arithmetic_v<V> && !std::is_same_v<V, bool>) {
ret.append(Psc::JSON(name, field.value()));
}
// --- optional<string>
else if constexpr (std::is_same_v<V, std::string>) {
ret.append(Psc::JSON(name, field.value()));
}
// --- optional<enum>
else if constexpr (std::is_enum_v<V>) {
ret.append(Psc::JSON(name, to_string(field.value())));
}
// --- optional<atomic>
else if constexpr (is_atomic_v<T> || is_copyable_atomic_v<T>) {
ret.append(Psc::JSON(name, field.value().load()));
}
// --- optional<未知类型>(调试输出)
else {
ret.append(Psc::JSON(name, field.value().to_string()));
}
}
else {
// optional 没值 → JSON null
ret.append(Psc::JSON(name, nullptr));
}
}
else if constexpr (is_shared_ptr_v<C>) {
if (field) {
ret.append(Psc::JSON(name, field->to_string()));
}
else {
ret.append(Psc::JSON(name, nullptr));
}
}
// 最终兜底
else {
ret.append(Psc::JSON(name, field.to_string()));
}
}
} // namespace Psc
#undef Json_GET
#define Get_J_ex(NAME, FIELD) \
{ \
std::error_code ec_##FIELD; \
const bool ok_##FIELD = \
Psc::assign_field_ec(FIELD, that_json, NAME, ec_##FIELD); \
Psc::check_json_assign_field(ok_##FIELD, ec_##FIELD, that_json, NAME); \
}
#define Ret_J_ex(NAME, FIELD) \
{ \
Psc::output_field(FIELD, ret, NAME); \
}
#define Get_J(FIELD) Get_J_ex(#FIELD, FIELD)
#define Ret_J(FIELD) Ret_J_ex(#FIELD, FIELD)
#define JSON_KV(P) {#P, (P)}
#define JSON_KV_1(P1) JSON_KV(P1)
#define JSON_KV_2(P1, P2) JSON_KV_1(P1), JSON_KV(P2)
#define JSON_KV_3(P1, P2, P3) JSON_KV_2(P1, P2), JSON_KV(P3)
#define JSON_KV_4(P1, P2, P3, P4) JSON_KV_3(P1, P2, P3), JSON_KV(P4)
#define JSON_KV_5(P1, P2, P3, P4, P5) JSON_KV_4(P1, P2, P3, P4), JSON_KV(P5)
#define JSON_KV_6(P1, P2, P3, P4, P5, P6) \
JSON_KV_5(P1, P2, P3, P4, P5), JSON_KV(P6)
#define JSON_KV_7(P1, P2, P3, P4, P5, P6, P7) \
JSON_KV_6(P1, P2, P3, P4, P5, P6), JSON_KV(P7)
#define JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8) \
JSON_KV_7(P1, P2, P3, P4, P5, P6, P7), JSON_KV(P8)
#define JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9) \
JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8), JSON_KV(P9)
#define JSON_KV_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \
JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9), JSON_KV(P10)
#define VAR_JSON_1(P1) Psc::JSON::object({JSON_KV_1(P1)})
#define VAR_JSON_2(P1, P2) Psc::JSON::object({JSON_KV_2(P1, P2)})
#define VAR_JSON_3(P1, P2, P3) Psc::JSON::object({JSON_KV_3(P1, P2, P3)})
#define VAR_JSON_4(P1, P2, P3, P4) \
Psc::JSON::object({JSON_KV_4(P1, P2, P3, P4)})
#define VAR_JSON_5(P1, P2, P3, P4, P5) \
Psc::JSON::object({JSON_KV_5(P1, P2, P3, P4, P5)})
#define VAR_JSON_6(P1, P2, P3, P4, P5, P6) \
Psc::JSON::object({JSON_KV_6(P1, P2, P3, P4, P5, P6)})
#define VAR_JSON_7(P1, P2, P3, P4, P5, P6, P7) \
Psc::JSON::object({JSON_KV_7(P1, P2, P3, P4, P5, P6, P7)})
#define VAR_JSON_8(P1, P2, P3, P4, P5, P6, P7, P8) \
Psc::JSON::object({JSON_KV_8(P1, P2, P3, P4, P5, P6, P7, P8)})
#define VAR_JSON_9(P1, P2, P3, P4, P5, P6, P7, P8, P9) \
Psc::JSON::object({JSON_KV_9(P1, P2, P3, P4, P5, P6, P7, P8, P9)})
#define VAR_JSON_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10) \
Psc::JSON::object({JSON_KV_10(P1, P2, P3, P4, P5, P6, P7, P8, P9, P10)})
#include "boost/pfr/core.hpp"
#include <boost/pfr/core_name.hpp>
#include <string_view>
#define PSC_TO_JSON \
[[nodiscard]] Psc::JSON to_base_json() const { \
Psc::JSON ret = Psc::JSON::object(); \
using T = std::remove_cv_t<std::remove_pointer_t<decltype(this)>>; \
boost::pfr::for_each_field(*this, [&, names = boost::pfr::names_as_array<T>(), i = std::size_t{}](const auto& field) mutable { \
Psc::output_field(field, ret, names[i++].data()); \
}); \
return ret; \
}
#define PSC_FROM_JSON \
void from_base_json(const Psc::JSON *that_json) { \
using T = std::remove_cv_t<std::remove_pointer_t<decltype(this)>>; \
boost::pfr::for_each_field(*this, [&, names = boost::pfr::names_as_array<T>(), i = std::size_t{}](auto& field) mutable { \
auto name = names[i++].data(); \
std::error_code ec; \
auto t = Psc::assign_field_ec(field, that_json, name, ec); \
Psc::check_json_assign_field(t, ec, that_json, name); \
}); \
}
#define PSC_USE_JSON \
PSC_TO_JSON \
PSC_FROM_JSON