501 lines
20 KiB
C++
501 lines
20 KiB
C++
#pragma once
|
||
|
||
#include "../system/export.h"
|
||
#include "../Base/global_include.h"
|
||
#include <atomic>
|
||
#include <functional>
|
||
#include <iostream>
|
||
#include <limits> // std::numeric_limits
|
||
#include <optional>
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
#define PSC_JSON_KEY_PARAMS(M, SEP) \
|
||
M(const std::string&, key)
|
||
|
||
#define PSC_JSON_PARSE_PARAMS(M, SEP) \
|
||
M(const std::string&, text)
|
||
|
||
#define PSC_JSON_FILE_PARAMS(M, SEP) \
|
||
M(const std::string&, file_name)
|
||
|
||
#undef max
|
||
#undef min
|
||
#define Json_GET(type, name) \
|
||
[[nodiscard]] type get_##name(const std::string &_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(const std::string& val, T_Number& out, std::error_code& ec) noexcept;
|
||
|
||
template <typename T_Number>
|
||
T_Number to_number(const std::string& 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(const std::string& 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(const std::string& 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
|
||
unsigned long long x = std::stoull(val);
|
||
// 额外范围检查,避免静默截断
|
||
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 {
|
||
long long x = std::stoll(val);
|
||
// 额外范围检查,避免静默截断
|
||
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
|
||
long double x = std::stold(val);
|
||
// 可选:检查是否超出目标浮点类型范围
|
||
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 *, const std::string &, const char *, int)> handle_error;
|
||
explicit operator std::string() const;
|
||
friend std::ostream &operator<<(std::ostream &os, const JSON &obj);
|
||
|
||
|
||
|
||
|
||
[[nodiscard]] bool has(const std::string &key) const;
|
||
[[nodiscard]] const JSON *get(const std::string &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(const std::string& 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(const std::string& 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(const std::string& 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 key, const JSON &body);
|
||
template <typename T,
|
||
typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, JSON>>>
|
||
JSON(std::string k, T&& v) {
|
||
key = std::move(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;
|
||
|
||
|
||
void append_list(const std::vector<JSON> &arr);
|
||
void append(const JSON &json);
|
||
void prepend(const JSON &json);
|
||
static JSON array(std::string key, const std::vector<JSON> &arr);
|
||
static JSON object(std::string 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(const std::string &object_split = "\n",
|
||
const std::string &array_split = "\n",
|
||
const std::string &indentStr = " ", const int &indent = 0) const;
|
||
|
||
private:
|
||
static std::string get_indent(const int &indent, const std::string &indentStr);
|
||
void append_value_string(std::string &ret, const int &indent, bool isEnd, const std::string &object_split,
|
||
const std::string &array_split, const std::string &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, 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>) {
|
||
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();
|
||
}
|
||
}
|
||
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_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>) {
|
||
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>) {
|
||
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()));
|
||
}
|
||
}
|
||
|
||
|
||
void check_json_assign_field(bool ok, const std::error_code& ec, const Psc::JSON *that_json, const char *name);
|
||
|
||
} // namespace Psc
|
||
|
||
|
||
|
||
|
||
#undef Json_GET
|
||
#define Get_J(FIELD) { \
|
||
std::error_code ec_##FIELD; \
|
||
const bool ok_##FIELD = Psc::assign_field_ec(FIELD, that_json, #FIELD, ec_##FIELD); \
|
||
Psc::check_json_assign_field(ok_##FIELD, ec_##FIELD, that_json, #FIELD); \
|
||
}
|
||
|
||
#define Ret_J(FIELD) Psc::output_field(FIELD, ret, #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) }) |