Files
CPP_Core/Core/Base/JSON.h
T
2026-06-16 10:56:40 +08:00

501 lines
20 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 "../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) })