diff --git a/Core/Base/JSON.cpp b/Core/Base/JSON.cpp index 629ea6d..f81ace5 100644 --- a/Core/Base/JSON.cpp +++ b/Core/Base/JSON.cpp @@ -6,563 +6,486 @@ #define HANDLE_ERROR(reason) \ JSON::handle_error(__func__, reason, __FILE__, __LINE__); namespace Psc { -std::function - JSON::handle_error = [](const char *function_name, - const std::string &reason, const char *file, - int line) { - std::ostringstream oss; - oss << "JSON::" << function_name << " 原因:" << reason << std::endl; - oss << "位置:" << file << ":" << line << std::endl; - std::cout << oss.str(); - Psc::fail_fast(); - }; - -void skip_whitespace(std::string &s, size_t &i); -bool parse_string(std::string &s, size_t &i, std::string &out, - std::error_code &ec) noexcept; -bool parse_number(std::string &s, size_t &i, std::string &out, - std::error_code &ec) noexcept; -bool parse_object(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept; -bool parse_array(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept; - -std::ostream &operator<<(std::ostream &os, const JSON &obj) { - os << const_cast(obj).to_json_string(); - return os; +std::function +JSON::handle_error = [](const char *function_name, + const std::string& reason, const char *file, + int line) { + std::ostringstream oss; + oss << "JSON::" << function_name << " 原因:" << reason << std::endl; + oss << "位置:" << file << ":" << line << std::endl; + std::cout << oss.str(); + Psc::fail_fast(); +}; +void skip_whitespace(std::string& s, size_t& i); +bool parse_string(std::string& s, size_t& i, std::string& out, + std::error_code& ec) noexcept; +bool parse_number(std::string& s, size_t& i, std::string& out, + std::error_code& ec) noexcept; +bool parse_object(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept; +bool parse_array(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept; +std::ostream& operator<<(std::ostream& os, const JSON& obj) { + os << const_cast(obj).to_json_string(); + return os; } - JSON::operator std::string() const { - return const_cast(this)->to_json_string(); + return const_cast(this)->to_json_string(); } - PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(std::string, JSON, get_string, PSC_JSON_KEY_PARAMS) - -bool JSON::get_string_ec(const std::string &key, std::string &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; - } - out = that->val; - return true; +bool JSON::get_string_ec(const std::string& key, std::string& 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; + } + out = that->val; + return true; } - PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL_0(bool, JSON, bool_val) - -bool JSON::bool_val_ec(bool &out, std::error_code &ec) const noexcept { - if (val != "true" && val != "false") { - // HANDLE_ERROR(key + " Invalid bool argument: " + val) - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - out = (val == "true"); - return true; +bool JSON::bool_val_ec(bool& out, std::error_code& ec) const noexcept { + if (val != "true" && val != "false") { + // HANDLE_ERROR(key + " Invalid bool argument: " + val) + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + out = (val == "true"); + return true; } - PSC_DEFINE_TRIPLE_API_CONST_METHOD_FROM_BOOL(bool, JSON, get_bool, PSC_JSON_KEY_PARAMS) - -bool JSON::get_bool_ec(const std::string &key, bool &out, - std::error_code &ec) const noexcept { - const Psc::JSON *that = get(key); - if (!that) { - // std::cout << "get" "bool" " error json not found! key:" << _key << - // " json: " << to_json_string() << std::endl; - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - return that->bool_val_ec(out, ec); +bool JSON::get_bool_ec(const std::string& key, bool& out, + std::error_code& ec) const noexcept { + const Psc::JSON *that = get(key); + if (!that) { + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + return that->bool_val_ec(out, ec); } - -// JSON::JSON(std::string key, const char* val) : key(std::move(key)), -// val(std::string(val)), valueType(String) { -// } -// -// JSON::JSON(std::string key, std::string val) : key(std::move(key)), -// val(std::move(val)), valueType(String) { -// } -// -// JSON::JSON(std::string key, bool val) : key(std::move(key)), val(val ? "true" -// : "false"), valueType(Bool) { -// } -// -JSON::JSON(std::string key, const JSON &children) - : key(std::move(key)), val(children.val), valueType(children.valueType), - children(children.children) {} - -void JSON::append_list(const std::vector &arr) { +JSON::JSON(std::string key, const JSON& children) : key(std::move(key)), val(children.val), valueType(children.valueType), + children(children.children) {} +void JSON::append_list(const std::vector& arr) { #ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } -#else - -#endif - for (const JSON &j : arr) { - children.push_back(j); - } -} - -void JSON::append(const JSON &json) { -#ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } #else #endif - children.push_back(json); + for (const JSON& j : arr) { + children.push_back(j); + } } -void JSON::prepend(const JSON &json) { +void JSON::append(const JSON& json) { #ifdef _DEBUG - if (valueType != JsonType::Object && valueType != JsonType::Array) { - HANDLE_ERROR("append_list error! key:" + key + " not array or object " + - to_json_string()) - } + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } #else #endif - children.insert(children.begin(), json); + children.push_back(json); } - -bool JSON::has(const std::string &_key) const { - for (const JSON &child : children) { - if (child.key == _key) { - return true; - } - } - return false; +void JSON::prepend(const JSON& json) { +#ifdef _DEBUG + if (valueType != JsonType::Object && valueType != JsonType::Array) { + HANDLE_ERROR("append_list error! key:" + key + " not array or object " + + to_json_string()) + } +#else +#endif + children.insert(children.begin(), json); } -const JSON *JSON::get(const std::string &_key) const { - for (const JSON &child : children) { - if (child.key == _key) { - return &child; - } - } - // HANDLE_ERROR(_key + " not found!" + to_json_string()) - return nullptr; +bool JSON::has(const std::string& _key) const { + for (const JSON& child : children) { + if (child.key == _key) { + return true; + } + } + return false; } - -JSON JSON::array(const std::vector &children) { - return array("", children); +const JSON* JSON::get(const std::string& _key) const { + for (const JSON& child : children) { + if (child.key == _key) { + return &child; + } + } + // HANDLE_ERROR(_key + " not found!" + to_json_string()) + return nullptr; } - -JSON JSON::object(const std::vector &children) { - return object("", children); +JSON JSON::array(const std::vector& children) { + return array("", children); } - -JSON JSON::array(std::string key, const std::vector &children) { - JSON ret; - ret.key = std::move(key); - ret.valueType = Array; - ret.children = children; - return ret; +JSON JSON::object(const std::vector& children) { + return object("", children); } - -JSON JSON::object(std::string key, const std::vector &children) { - JSON ret; - ret.key = std::move(key); - ret.valueType = Object; - ret.children = children; - return ret; +JSON JSON::array(std::string key, const std::vector& children) { + JSON ret; + ret.key = std::move(key); + ret.valueType = Array; + ret.children = children; + return ret; } - -std::string JSON::get_indent(const int &indent, const std::string &indentStr) { - std::string ret; - for (int i = 0; i < indent; ++i) { - ret.append(indentStr); - } - return ret; +JSON JSON::object(std::string key, const std::vector& children) { + JSON ret; + ret.key = std::move(key); + ret.valueType = Object; + ret.children = children; + return ret; } - -void JSON::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, +std::string JSON::get_indent(const int& indent, const std::string& indentStr) { + std::string ret; + for (int i = 0; i < indent; ++i) { + ret.append(indentStr); + } + return ret; +} +void JSON::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 { - ret.append(get_indent(indent, indentStr)); - if (fatherType == Object) - ret.append("\"" + key + "\"" + ": "); - const std::string split = fatherType == Array ? array_split : object_split; - if (valueType == String) { - ret.append("\"" + val + "\""); - goto end; - } - if (valueType == Null) { - ret.append("null"); - goto end; - } - if (valueType == Number || valueType == Bool) { - ret.append(val); - goto end; - } - if (valueType == Object) { - ret.append("{" + object_split); - size_t n = children.size(); - if (n) { - for (size_t i = 0; i < n - 1; ++i) { - children[i].append_value_string(ret, indent + 2, false, object_split, - array_split, indentStr, Object); - } - children[n - 1].append_value_string(ret, indent + 2, true, object_split, - array_split, indentStr, Object); - } - ret.append(get_indent(indent, indentStr) + "}"); - goto end; - } - if (valueType == Array) { - ret.append("[" + array_split); - size_t n = children.size(); - if (n) { - for (size_t i = 0; i < n - 1; ++i) { - children[i].append_value_string(ret, indent + 1, false, object_split, - array_split, indentStr, Array); - } - children[n - 1].append_value_string(ret, indent + 1, true, object_split, - array_split, indentStr, Array); - } - ret.append(get_indent(indent, indentStr) + "]"); - goto end; - } + ret.append(get_indent(indent, indentStr)); + if (fatherType == Object) ret.append("\"" + key + "\"" + ": "); + const std::string split = fatherType == Array ? array_split : object_split; + if (valueType == String) { + ret.append("\"" + val + "\""); + goto end; + } + if (valueType == Null) { + ret.append("null"); + goto end; + } + if (valueType == Number || valueType == Bool) { + ret.append(val); + goto end; + } + if (valueType == Object) { + ret.append("{" + object_split); + size_t n = children.size(); + if (n) { + for (size_t i = 0; i < n - 1; ++i) { + children[i].append_value_string(ret, indent + 2, false, object_split, + array_split, indentStr, Object); + } + children[n - 1].append_value_string(ret, indent + 2, true, object_split, + array_split, indentStr, Object); + } + ret.append(get_indent(indent, indentStr) + "}"); + goto end; + } + if (valueType == Array) { + ret.append("[" + array_split); + size_t n = children.size(); + if (n) { + for (size_t i = 0; i < n - 1; ++i) { + children[i].append_value_string(ret, indent + 1, false, object_split, + array_split, indentStr, Array); + } + children[n - 1].append_value_string(ret, indent + 1, true, object_split, + array_split, indentStr, Array); + } + ret.append(get_indent(indent, indentStr) + "]"); + goto end; + } end: - if (!isEnd) - ret.append(","); - ret.append(split); + if (!isEnd) ret.append(","); + ret.append(split); } - -void handleBlank(std::string &s) { - int n = static_cast(s.length()); - bool isInStr = false; - for (int i = n - 1; i >= 0; --i) { - const char &c = s[i]; - if (c == '\"') - isInStr = !isInStr; - if (isInStr) - continue; - if (c == '\n' || c == '\t' || c == ' ') { - s.erase(i, 1); - } - } +void handleBlank(std::string& s) { + int n = static_cast(s.length()); + bool isInStr = false; + for (int i = n - 1; i >= 0; --i) { + const char& c = s[i]; + if (c == '\"') isInStr = !isInStr; + if (isInStr) continue; + if (c == '\n' || c == '\t' || c == ' ') { + s.erase(i, 1); + } + } } - -std::string JSON::to_json_string(const std::string &object_split, - const std::string &array_split, - const std::string &indentStr, - const int &indent) const { - if (valueType == Bool) - return val; - if (valueType == String) - return '\"' + val + '\"'; - if (valueType == Number) - return val; - if (valueType == Null) { - if (children.size() != 0) { - HANDLE_ERROR("null 带有children 转成了字符串 (" + key + "," + val + ")" + - to_json_string()) - } - return "null"; - } - std::string ret; - // ret.append(split); - append_value_string(ret, indent, true, object_split, array_split, indentStr, - Null); - return ret; +std::string JSON::to_json_string(const std::string& object_split, + const std::string& array_split, + const std::string& indentStr, + const int& indent) const { + if (valueType == Bool) return val; + if (valueType == String) return '\"' + val + '\"'; + if (valueType == Number) return val; + if (valueType == Null) { + if (children.size() != 0) { + HANDLE_ERROR("null 带有children 转成了字符串 (" + key + "," + val + ")" + + to_json_string()) + } + return "null"; + } + std::string ret; + append_value_string(ret, indent, true, object_split, array_split, indentStr, + Null); + return ret; } - JSON::JSON() = default; - -void copy(JSON &dest, const JSON &src) { - dest.key = src.key; - dest.valueType = src.valueType; - dest.val = src.val; - auto len = src.children.size(); - dest.children.resize(len); - for (size_t i = 0; i < len; ++i) { - dest.children[i] = src.children[i]; - } +void copy(JSON& dest, const JSON& src) { + dest.key = src.key; + dest.valueType = src.valueType; + dest.val = src.val; + auto len = src.children.size(); + dest.children.resize(len); + for (size_t i = 0; i < len; ++i) { + dest.children[i] = src.children[i]; + } } - -JSON::JSON(const JSON &other) { copy(*this, other); } -JSON::JSON(JSON &&other) noexcept { - key = std::move(other.key); - valueType = std::move(other.valueType); - children = std::move(other.children); - val = std::move(other.val); +JSON::JSON(const JSON& other) { + copy(*this, other); } - -JSON &JSON::operator=(const JSON &other) { - if (this != &other) { // 防止自赋值 - copy(*this, other); // 调用拷贝函数 - } - return *this; +JSON::JSON(JSON&& other) noexcept { + key = std::move(other.key); + valueType = std::move(other.valueType); + children = std::move(other.children); + val = std::move(other.val); } - -JSON &JSON::operator=(JSON &&other) noexcept { - if (this != &other) { - key = std::move(other.key); - valueType = other.valueType; - children = std::move(other.children); - val = std::move(other.val); - } - return *this; +JSON& JSON::operator=(const JSON& other) { + if (this != &other) { + // 防止自赋值 + copy(*this, other); // 调用拷贝函数 + } + return *this; } -JSON& JSON::operator+=(const JSON& child) { - append_list(child.children); - return *this; +JSON& JSON::operator=(JSON&& other) noexcept { + if (this != &other) { + key = std::move(other.key); + valueType = other.valueType; + children = std::move(other.children); + val = std::move(other.val); + } + return *this; } -JSON& JSON::operator+=(JSON&& child) { - append_list(child.children); - return *this; +JSON& JSON::operator+=(const JSON& child) { + append_list(child.children); + return *this; +} +JSON& JSON::operator+=(JSON&& child) { + append_list(child.children); + return *this; } - // 解析的分割线---------------------------- - // 如果未找到非空白字符,将 i 更新为 std::string::npos -void skip_whitespace(std::string &s, size_t &i) { - i = s.find_first_not_of(" \t\n\r\f\v", i); +void skip_whitespace(std::string& s, size_t& i) { + i = s.find_first_not_of(" \t\n\r\f\v", i); } - // 传入的 i 是 "所在的index -bool parse_string(std::string &s, size_t &i, std::string &out, - std::error_code &ec) noexcept { - ec.clear(); - i++; // skip '"' - if (s[i] == '"') { - i++; - out.clear(); - return true; - } - std::string ret; - while (true) { - if (s[i] == '"' && s[i - 1] != '\\') { - break; - } - ret += s[i]; - i++; - } - i++; - out = std::move(ret); - return true; +bool parse_string(std::string& s, size_t& i, std::string& out, + std::error_code& ec) noexcept { + ec.clear(); + i++; // skip '"' + if (s[i] == '"') { + i++; + out.clear(); + return true; + } + std::string ret; + while (true) { + if (s[i] == '"' && s[i - 1] != '\\') { + break; + } + ret += s[i]; + i++; + } + i++; + out = std::move(ret); + return true; } - -bool parse_number(std::string &s, size_t &i, std::string &out, - std::error_code &ec) noexcept { - ec.clear(); - std::string num; - while (isdigit(s[i])) { - num += s[i++]; - } - if (s[i] == '.') { - i++; - num += '.'; - while (isdigit(s[i])) { - num += s[i++]; - } - } - out = std::move(num); - return true; +bool parse_number(std::string& s, size_t& i, std::string& out, + std::error_code& ec) noexcept { + ec.clear(); + std::string num; + while (isdigit(s[i])) { + num += s[i++]; + } + if (s[i] == '.') { + i++; + num += '.'; + while (isdigit(s[i])) { + num += s[i++]; + } + } + out = std::move(num); + return true; } - -bool parse_json_EX(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept; +bool parse_json_EX(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept; PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json, PSC_JSON_PARSE_PARAMS) - -bool parse_json_ec(const std::string &text, JSON &out, - std::error_code &ec) noexcept { - auto &t = const_cast(text); - size_t i = 0; - return parse_json_EX(t, i, out, ec); +bool parse_json_ec(const std::string& text, JSON& out, + std::error_code& ec) noexcept { + auto& t = const_cast(text); + size_t i = 0; + return parse_json_EX(t, i, out, ec); } - -bool parse_json_EX(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept { - skip_whitespace(s, i); - switch (s[i]) { - case 'n': - i += 4; - out = JSON(nullptr); - return true; - case 't': - i += 4; - out = JSON(true); - return true; - case 'f': - i += 5; - out = JSON(false); - return true; - case '{': - return parse_object(s, i, out, ec); - case '[': - return parse_array(s, i, out, ec); - case '"': { - std::string str; - if (!parse_string(s, i, str, ec)) - return false; - out = JSON(std::move(str)); - return true; - } - case '-': { - i++; // skip - - JSON ret(nullptr); - ret.valueType = Number; - std::string num; - if (!parse_number(s, i, num, ec)) - return false; - ret.val = '-' + num; - out = std::move(ret); - return true; - } - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - JSON ret(nullptr); - ret.valueType = Number; - std::string num; - if (!parse_number(s, i, num, ec)) - return false; - ret.val = std::move(num); - out = std::move(ret); - return true; - } - default: { - // HANDLE_ERROR("json 解析错误! " + s + "\n") - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - } +bool parse_json_EX(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept { + skip_whitespace(s, i); + switch (s[i]) { + case 'n': i += 4; + out = JSON(nullptr); + return true; + case 't': i += 4; + out = JSON(true); + return true; + case 'f': i += 5; + out = JSON(false); + return true; + case '{': return parse_object(s, i, out, ec); + case '[': return parse_array(s, i, out, ec); + case '"': { + std::string str; + if (!parse_string(s, i, str, ec)) return false; + out = JSON(std::move(str)); + return true; + } + case '-': { + i++; // skip - + JSON ret(nullptr); + ret.valueType = Number; + std::string num; + if (!parse_number(s, i, num, ec)) return false; + ret.val = '-' + num; + out = std::move(ret); + return true; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + JSON ret(nullptr); + ret.valueType = Number; + std::string num; + if (!parse_number(s, i, num, ec)) return false; + ret.val = std::move(num); + out = std::move(ret); + return true; + } + default: { + // HANDLE_ERROR("json 解析错误! " + s + "\n") + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + } } - -bool parse_object(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept { - i++; // skip '{' - skip_whitespace(s, i); - JSON ret = JSON::object(); - if (s[i] == '}') { - i++; - out = std::move(ret); - return true; - } - while (true) { - skip_whitespace(s, i); - std::string key; - if (!parse_string(s, i, key, ec)) - return false; - skip_whitespace(s, i); - if (s[i] != ':') { - ec = std::make_error_code(std::errc::invalid_argument); - return false; - } - i++; - skip_whitespace(s, i); - JSON kv; - if (!parse_json_EX(s, i, kv, ec)) - return false; - skip_whitespace(s, i); - kv.key = key; - ret.children.push_back(std::move(kv)); - if (s[i] == '}') { - i++; - break; - } - if (s[i] == ',') { - i++; - } - } - out = std::move(ret); - return true; +bool parse_object(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept { + i++; // skip '{' + skip_whitespace(s, i); + JSON ret = JSON::object(); + if (s[i] == '}') { + i++; + out = std::move(ret); + return true; + } + while (true) { + skip_whitespace(s, i); + std::string key; + if (!parse_string(s, i, key, ec)) return false; + skip_whitespace(s, i); + if (s[i] != ':') { + ec = std::make_error_code(std::errc::invalid_argument); + return false; + } + i++; + skip_whitespace(s, i); + JSON kv; + if (!parse_json_EX(s, i, kv, ec)) return false; + skip_whitespace(s, i); + kv.key = key; + ret.children.push_back(std::move(kv)); + if (s[i] == '}') { + i++; + break; + } + if (s[i] == ',') { + i++; + } + } + out = std::move(ret); + return true; } - -bool parse_array(std::string &s, size_t &i, JSON &out, - std::error_code &ec) noexcept { - i++; // skip '[' - skip_whitespace(s, i); - JSON ret = JSON::array(); - if (s[i] == ']') { - i++; - out = std::move(ret); - return true; - } - while (true) { - skip_whitespace(s, i); - // parseJson(s, i) - JSON kv; - if (!parse_json_EX(s, i, kv, ec)) - return false; - ret.children.push_back(std::move(kv)); - skip_whitespace(s, i); - if (s[i] == ']') { - i++; - break; - } - if (s[i] == ',') { - i++; - } - } - out = std::move(ret); - return true; +bool parse_array(std::string& s, size_t& i, JSON& out, + std::error_code& ec) noexcept { + i++; // skip '[' + skip_whitespace(s, i); + JSON ret = JSON::array(); + if (s[i] == ']') { + i++; + out = std::move(ret); + return true; + } + while (true) { + skip_whitespace(s, i); + // parseJson(s, i) + JSON kv; + if (!parse_json_EX(s, i, kv, ec)) return false; + ret.children.push_back(std::move(kv)); + skip_whitespace(s, i); + if (s[i] == ']') { + i++; + break; + } + if (s[i] == ',') { + i++; + } + } + out = std::move(ret); + return true; } - PSC_DEFINE_TRIPLE_API_FROM_BOOL(JSON, parse_json_file, PSC_JSON_FILE_PARAMS) - -bool parse_json_file_ec(const std::string &file_name, JSON &out, - std::error_code &ec) noexcept { - auto readFile = [](const std::string &path, std::string &content, - std::error_code &ec) noexcept { - std::ifstream infile(path, std::ios::binary | std::ios::ate); - if (!infile.is_open()) { - ec = std::make_error_code(std::errc::no_such_file_or_directory); - return false; - } - - std::streamsize fileSize = infile.tellg(); - if (fileSize < 0) { - ec = std::make_error_code(std::errc::io_error); - return false; - } - - infile.seekg(0, std::ios::beg); - content.resize(static_cast(fileSize)); - if (!infile.read(content.data(), fileSize)) { - ec = std::make_error_code(std::errc::io_error); - return false; - } - - return true; - }; - - std::string text; - if (!readFile(file_name, text, ec)) - return false; - return parse_json_ec(text, out, ec); +bool parse_json_file_ec(const std::string& file_name, JSON& out, + std::error_code& ec) noexcept { + auto readFile = [](const std::string& path, std::string& content, + std::error_code& ec) noexcept { + std::ifstream infile(path, std::ios::binary | std::ios::ate); + if (!infile.is_open()) { + ec = std::make_error_code(std::errc::no_such_file_or_directory); + return false; + } + std::streamsize fileSize = infile.tellg(); + if (fileSize < 0) { + ec = std::make_error_code(std::errc::io_error); + return false; + } + infile.seekg(0, std::ios::beg); + content.resize(static_cast(fileSize)); + if (!infile.read(content.data(), fileSize)) { + ec = std::make_error_code(std::errc::io_error); + return false; + } + return true; + }; + std::string text; + if (!readFile(file_name, text, ec)) return false; + return parse_json_ec(text, out, ec); } - -void check_json_assign_field(bool ok, const std::error_code &ec, +void check_json_assign_field(bool ok, const std::error_code& ec, const Psc::JSON *that_json, const char *name) { - if (!ok) { - std::ostringstream oss; - oss << "json assign error:[" << name << "," - << Psc::platform_2_utf8(ec.message()) << "]"; - if (that_json != nullptr) { - oss << " from" << that_json->to_json_string(); - } - oss << std::endl; - throw json_assign_error(ec, oss.str()); - } + if (!ok) { + std::ostringstream oss; + oss << "json assign error:[" << name << "," + << Psc::platform_2_utf8(ec.message()) << "]"; + if (that_json != nullptr) { + oss << " from" << that_json->to_json_string(); + } + oss << std::endl; + throw json_assign_error(ec, oss.str()); + } } } // namespace Psc - #undef HANDLE_ERROR - #ifdef _USE_GTEST #include #include @@ -571,34 +494,34 @@ void check_json_assign_field(bool ok, const std::error_code &ec, #include #include #include - namespace { - -std::string compact_json(const Psc::JSON &json) { - return json.to_json_string("", "", "", 0); +std::string compact_json(const Psc::JSON& json) { + return json.to_json_string("", "", "", 0); } - struct TempFileGuard { - const char *path; - - ~TempFileGuard() { std::remove(path); } + const char *path; + ~TempFileGuard() { + std::remove(path); + } }; - struct JsonCase { - const char *name; - const char *text; + const char *name; + const char *text; }; - -const std::vector &json_cases() { - static const std::vector cases = {{"object_basic", - R"({ +const std::vector& json_cases() { + static const std::vector cases = { + { + "object_basic", + R"({ "name": "psc", "debug": true, "version": 1, "empty": null - })"}, - {"nested_object", - R"({ + })" + }, + { + "nested_object", + R"({ "project": { "name": "psc", "enabled": true, @@ -609,18 +532,22 @@ const std::vector &json_cases() { } }, "status": "ok" - })"}, - {"array_with_objects", - R"({ + })" + }, + { + "array_with_objects", + R"({ "users": [ {"name": "tom", "admin": true}, {"name": "jerry", "admin": false}, {"name": "alice", "admin": true} ], "count": 3 - })"}, - {"deep_mixed", - R"({ + })" + }, + { + "deep_mixed", + R"({ "app": { "name": "server", "modules": [ @@ -648,9 +575,11 @@ const std::vector &json_cases() { }, "ok": true, "none": null - })"}, - {"root_array", - R"([ + })" + }, + { + "root_array", + R"([ { "name": "one", "value": 1, @@ -670,72 +599,59 @@ const std::vector &json_cases() { false, 123, "text" - ])"}}; - - return cases; + ])" + } + }; + return cases; } - -void assert_parse_ok(const char *text, Psc::JSON &out) { - std::error_code ec; - ASSERT_TRUE(Psc::parse_json_ec(text, out, ec)) << ec.message(); - ASSERT_FALSE(ec) << ec.message(); +void assert_parse_ok(const char *text, Psc::JSON& out) { + std::error_code ec; + ASSERT_TRUE(Psc::parse_json_ec(text, out, ec)) << ec.message(); + ASSERT_FALSE(ec) << ec.message(); } - void assert_round_trip(const char *text) { - Psc::JSON first; - assert_parse_ok(text, first); - - const std::string first_text = compact_json(first); - - Psc::JSON second; - assert_parse_ok(first_text.c_str(), second); - - const std::string second_text = compact_json(second); - - EXPECT_EQ(second_text, first_text); + Psc::JSON first; + assert_parse_ok(text, first); + const std::string first_text = compact_json(first); + Psc::JSON second; + assert_parse_ok(first_text.c_str(), second); + const std::string second_text = compact_json(second); + EXPECT_EQ(second_text, first_text); } - } // namespace - TEST(JSONTest, ParseAndRoundTripComplexJsonList) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - assert_round_trip(item.text); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + assert_round_trip(item.text); + } } - TEST(JSONTest, ParseObjectAndGetFields) { - Psc::JSON json; - assert_parse_ok( - R"({ + Psc::JSON json; + assert_parse_ok( + R"({ "name": "psc", "debug": true, "version": 1, "empty": null })", - json); - - EXPECT_TRUE(json.has("name")); - EXPECT_TRUE(json.has("debug")); - EXPECT_TRUE(json.has("version")); - EXPECT_TRUE(json.has("empty")); - EXPECT_FALSE(json.has("missing")); - - std::error_code ec; - - std::string name; - ASSERT_TRUE(json.get_string_ec("name", name, ec)); - EXPECT_EQ(name, "psc"); - - bool debug = false; - ASSERT_TRUE(json.get_bool_ec("debug", debug, ec)); - EXPECT_TRUE(debug); + json); + EXPECT_TRUE(json.has("name")); + EXPECT_TRUE(json.has("debug")); + EXPECT_TRUE(json.has("version")); + EXPECT_TRUE(json.has("empty")); + EXPECT_FALSE(json.has("missing")); + std::error_code ec; + std::string name; + ASSERT_TRUE(json.get_string_ec("name", name, ec)); + EXPECT_EQ(name, "psc"); + bool debug = false; + ASSERT_TRUE(json.get_bool_ec("debug", debug, ec)); + EXPECT_TRUE(debug); } - TEST(JSONTest, ParseNestedObjectAndGetTopFields) { - Psc::JSON json; - assert_parse_ok( - R"({ + Psc::JSON json; + assert_parse_ok( + R"({ "project": { "name": "psc", "enabled": true, @@ -746,163 +662,114 @@ TEST(JSONTest, ParseNestedObjectAndGetTopFields) { }, "status": "ok" })", - json); - - EXPECT_TRUE(json.has("project")); - EXPECT_TRUE(json.has("status")); - - std::error_code ec; - std::string status; - - ASSERT_TRUE(json.get_string_ec("status", status, ec)); - EXPECT_EQ(status, "ok"); - - const Psc::JSON *project = json.get("project"); - ASSERT_NE(project, nullptr); - - EXPECT_TRUE(project->has("name")); - EXPECT_TRUE(project->has("enabled")); - EXPECT_TRUE(project->has("config")); - - std::string project_name; - ASSERT_TRUE(project->get_string_ec("name", project_name, ec)); - EXPECT_EQ(project_name, "psc"); - - bool enabled = false; - ASSERT_TRUE(project->get_bool_ec("enabled", enabled, ec)); - EXPECT_TRUE(enabled); + json); + EXPECT_TRUE(json.has("project")); + EXPECT_TRUE(json.has("status")); + std::error_code ec; + std::string status; + ASSERT_TRUE(json.get_string_ec("status", status, ec)); + EXPECT_EQ(status, "ok"); + const Psc::JSON *project = json.get("project"); + ASSERT_NE(project, nullptr); + EXPECT_TRUE(project->has("name")); + EXPECT_TRUE(project->has("enabled")); + EXPECT_TRUE(project->has("config")); + std::string project_name; + ASSERT_TRUE(project->get_string_ec("name", project_name, ec)); + EXPECT_EQ(project_name, "psc"); + bool enabled = false; + ASSERT_TRUE(project->get_bool_ec("enabled", enabled, ec)); + EXPECT_TRUE(enabled); } - TEST(JSONTest, CopyConstructorKeepsComplexJson) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - - Psc::JSON original; - assert_parse_ok(item.text, original); - - const std::string expected = compact_json(original); - - Psc::JSON copied(original); - - EXPECT_EQ(compact_json(copied), expected); - - original = Psc::JSON{}; - assert_parse_ok(R"({"changed":true})", original); - - EXPECT_EQ(compact_json(copied), expected); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + Psc::JSON original; + assert_parse_ok(item.text, original); + const std::string expected = compact_json(original); + Psc::JSON copied(original); + EXPECT_EQ(compact_json(copied), expected); + original = Psc::JSON{}; + assert_parse_ok(R"({"changed":true})", original); + EXPECT_EQ(compact_json(copied), expected); + } } - TEST(JSONTest, CopyAssignmentKeepsComplexJson) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - - Psc::JSON source; - Psc::JSON target; - - assert_parse_ok(item.text, source); - assert_parse_ok(R"({"old":false})", target); - - const std::string expected = compact_json(source); - - target = source; - - EXPECT_EQ(compact_json(target), expected); - - source = Psc::JSON{}; - assert_parse_ok(R"({"changed":true})", source); - - EXPECT_EQ(compact_json(target), expected); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + Psc::JSON source; + Psc::JSON target; + assert_parse_ok(item.text, source); + assert_parse_ok(R"({"old":false})", target); + const std::string expected = compact_json(source); + target = source; + EXPECT_EQ(compact_json(target), expected); + source = Psc::JSON{}; + assert_parse_ok(R"({"changed":true})", source); + EXPECT_EQ(compact_json(target), expected); + } } - TEST(JSONTest, MoveConstructorKeepsComplexJson) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - - Psc::JSON source; - assert_parse_ok(item.text, source); - - const std::string expected = compact_json(source); - - Psc::JSON moved(std::move(source)); - - EXPECT_EQ(compact_json(moved), expected); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + Psc::JSON source; + assert_parse_ok(item.text, source); + const std::string expected = compact_json(source); + Psc::JSON moved(std::move(source)); + EXPECT_EQ(compact_json(moved), expected); + } } - TEST(JSONTest, MoveAssignmentKeepsComplexJson) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - - Psc::JSON source; - Psc::JSON target; - - assert_parse_ok(item.text, source); - assert_parse_ok(R"({"old":false})", target); - - const std::string expected = compact_json(source); - - target = std::move(source); - - EXPECT_EQ(compact_json(target), expected); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + Psc::JSON source; + Psc::JSON target; + assert_parse_ok(item.text, source); + assert_parse_ok(R"({"old":false})", target); + const std::string expected = compact_json(source); + target = std::move(source); + EXPECT_EQ(compact_json(target), expected); + } } - TEST(JSONTest, SelfCopyAssignmentKeepsComplexJson) { - for (const JsonCase &item : json_cases()) { - SCOPED_TRACE(item.name); - - Psc::JSON json; - assert_parse_ok(item.text, json); - - const std::string expected = compact_json(json); - - json = json; - - EXPECT_EQ(compact_json(json), expected); - } + for (const JsonCase& item : json_cases()) { + SCOPED_TRACE(item.name); + Psc::JSON json; + assert_parse_ok(item.text, json); + const std::string expected = compact_json(json); + json = json; + EXPECT_EQ(compact_json(json), expected); + } } - TEST(JSONTest, GetMissingFieldReturnsError) { - Psc::JSON json; - assert_parse_ok(R"({"name":"psc"})", json); - - std::error_code ec; - std::string value; - - EXPECT_FALSE(json.get_string_ec("missing", value, ec)); - EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); + Psc::JSON json; + assert_parse_ok(R"({"name":"psc"})", json); + std::error_code ec; + std::string value; + EXPECT_FALSE(json.get_string_ec("missing", value, ec)); + EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); } - TEST(JSONTest, GetBoolFromNonBoolReturnsError) { - Psc::JSON json; - assert_parse_ok(R"({"name":"psc"})", json); - - std::error_code ec; - bool value = false; - - EXPECT_FALSE(json.get_bool_ec("name", value, ec)); - EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); + Psc::JSON json; + assert_parse_ok(R"({"name":"psc"})", json); + std::error_code ec; + bool value = false; + EXPECT_FALSE(json.get_bool_ec("name", value, ec)); + EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); } - TEST(JSONTest, ParseInvalidJsonReturnsError) { - Psc::JSON json; - std::error_code ec; - - EXPECT_FALSE(Psc::parse_json_ec("x", json, ec)); - EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); + Psc::JSON json; + std::error_code ec; + EXPECT_FALSE(Psc::parse_json_ec("x", json, ec)); + EXPECT_EQ(ec, std::make_error_code(std::errc::invalid_argument)); } - TEST(JSONTest, ParseJsonFileComplex) { - constexpr const char *file_name = "psc_json_gtest_tmp.json"; - TempFileGuard guard{file_name}; - - { - std::ofstream ofs(file_name, std::ios::binary); - ASSERT_TRUE(ofs.is_open()); - - ofs << R"({ + constexpr const char *file_name = "psc_json_gtest_tmp.json"; + TempFileGuard guard{file_name}; + { + std::ofstream ofs(file_name, std::ios::binary); + ASSERT_TRUE(ofs.is_open()); + ofs << R"({ "app": { "name": "file_test", "enabled": true, @@ -913,40 +780,29 @@ TEST(JSONTest, ParseJsonFileComplex) { }, "ok": true })"; - } - - Psc::JSON json; - std::error_code ec; - - ASSERT_TRUE(Psc::parse_json_file_ec(file_name, json, ec)) << ec.message(); - - EXPECT_TRUE(json.has("app")); - EXPECT_TRUE(json.has("ok")); - - bool ok = false; - ASSERT_TRUE(json.get_bool_ec("ok", ok, ec)); - EXPECT_TRUE(ok); - - const Psc::JSON *app = json.get("app"); - ASSERT_NE(app, nullptr); - - std::string name; - ASSERT_TRUE(app->get_string_ec("name", name, ec)); - EXPECT_EQ(name, "file_test"); - - bool enabled = false; - ASSERT_TRUE(app->get_bool_ec("enabled", enabled, ec)); - EXPECT_TRUE(enabled); + } + Psc::JSON json; + std::error_code ec; + ASSERT_TRUE(Psc::parse_json_file_ec(file_name, json, ec)) << ec.message(); + EXPECT_TRUE(json.has("app")); + EXPECT_TRUE(json.has("ok")); + bool ok = false; + ASSERT_TRUE(json.get_bool_ec("ok", ok, ec)); + EXPECT_TRUE(ok); + const Psc::JSON *app = json.get("app"); + ASSERT_NE(app, nullptr); + std::string name; + ASSERT_TRUE(app->get_string_ec("name", name, ec)); + EXPECT_EQ(name, "file_test"); + bool enabled = false; + ASSERT_TRUE(app->get_bool_ec("enabled", enabled, ec)); + EXPECT_TRUE(enabled); } - TEST(JSONTest, ParseMissingFileReturnsError) { - Psc::JSON json; - std::error_code ec; - - EXPECT_FALSE(Psc::parse_json_file_ec("__not_exist_psc_json_test_file__.json", - json, ec)); - - EXPECT_EQ(ec, std::make_error_code(std::errc::no_such_file_or_directory)); + Psc::JSON json; + std::error_code ec; + EXPECT_FALSE(Psc::parse_json_file_ec("__not_exist_psc_json_test_file__.json", + json, ec)); + EXPECT_EQ(ec, std::make_error_code(std::errc::no_such_file_or_directory)); } - -#endif \ No newline at end of file +#endif diff --git a/Core/Base/JSON.h b/Core/Base/JSON.h index df4b7ca..e12c4c5 100644 --- a/Core/Base/JSON.h +++ b/Core/Base/JSON.h @@ -32,7 +32,7 @@ namespace Psc { class json_assign_error final : public std::system_error { public: - using std::system_error::system_error; + using std::system_error::system_error; }; enum JsonType : uint8_t { String, Object, Array, Null, Number, Bool }; template @@ -42,439 +42,423 @@ bool to_number_ec(const std::string& val, T_Number& out, std::error_code& ec) noexcept; template T_Number to_number(const std::string& val) { - return detail::triple_api_throwing( - [&val](T_Number& out, std::error_code& ec) { - return to_number_ec(val, out, ec); - }, - "to_number failed"); + return detail::triple_api_throwing( + [&val](T_Number& out, std::error_code& ec) { + return to_number_ec(val, out, ec); + }, + "to_number failed"); } template -expected -try_to_number(const std::string& val) noexcept { - return detail::triple_api_expected( - [&val](T_Number& out, std::error_code& ec) { - return to_number_ec(val, out, ec); - }); +expected try_to_number(const std::string& val) noexcept { + return detail::triple_api_expected( + [&val](T_Number& out, std::error_code& ec) { + return to_number_ec(val, out, ec); + }); } template bool to_number_ec(const std::string& val, T_Number& out, std::error_code& ec) noexcept { - ec.clear(); - // 限定只支持整数/浮点,其他类型编译期报错(比运行时返回默认值更安全) - static_assert(std::is_integral_v || - std::is_floating_point_v, - "to_number: T_Number must be an integral or " - "floating-point type"); - try { - if constexpr (std::is_integral_v) { - if constexpr (std::is_unsigned_v) { - // stoull: 负号、非数字等会抛 invalid_argument;超范围抛 out_of_range - unsigned long long x = std::stoull(val); - // 额外范围检查,避免静默截断 - if (x > static_cast( - std::numeric_limits::max())) { - ec = std::make_error_code(std::errc::result_out_of_range); - return false; - } - out = static_cast(x); - return true; - } - else { - long long x = std::stoll(val); - // 额外范围检查,避免静默截断 - if (x < static_cast(std::numeric_limits::min()) || - x > static_cast(std::numeric_limits::max())) { - ec = std::make_error_code(std::errc::result_out_of_range); - return false; - } - out = static_cast(x); - return true; - } - } - else { - // floating point - long double x = std::stold(val); - // 可选:检查是否超出目标浮点类型范围 - if (x < -static_cast(std::numeric_limits::max()) || - x > static_cast(std::numeric_limits::max())) { - ec = std::make_error_code(std::errc::result_out_of_range); - return false; - } - out = static_cast(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; // 数值超范围 - } + ec.clear(); + // 限定只支持整数/浮点,其他类型编译期报错(比运行时返回默认值更安全) + static_assert(std::is_integral_v || + std::is_floating_point_v, + "to_number: T_Number must be an integral or " + "floating-point type"); + try { + if constexpr (std::is_integral_v) { + if constexpr (std::is_unsigned_v) { + // stoull: 负号、非数字等会抛 invalid_argument;超范围抛 out_of_range + unsigned long long x = std::stoull(val); + // 额外范围检查,避免静默截断 + if (x > static_cast( + std::numeric_limits::max())) { + ec = std::make_error_code(std::errc::result_out_of_range); + return false; + } + out = static_cast(x); + return true; + } + else { + long long x = std::stoll(val); + // 额外范围检查,避免静默截断 + if (x < static_cast(std::numeric_limits::min()) || + x > static_cast(std::numeric_limits::max())) { + ec = std::make_error_code(std::errc::result_out_of_range); + return false; + } + out = static_cast(x); + return true; + } + } + else { + // floating point + long double x = std::stold(val); + // 可选:检查是否超出目标浮点类型范围 + if (x < -static_cast(std::numeric_limits::max()) || + x > static_cast(std::numeric_limits::max())) { + ec = std::make_error_code(std::errc::result_out_of_range); + return false; + } + out = static_cast(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 - 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 - T_Number get_number(const std::string& key) const { - return detail::triple_api_throwing( - [this, &key](T_Number& out, std::error_code& ec) { - return get_number_ec(key, out, ec); - }, - "get_number failed"); - } - template - expected - try_get_number(const std::string& key) const noexcept { - return detail::triple_api_expected( - [this, &key](T_Number& out, std::error_code& ec) { - return get_number_ec(key, out, ec); - }); - } - template - 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 - T_Number number_val() const { - return detail::triple_api_throwing( - [this](T_Number& out, std::error_code& ec) { - return number_val_ec(out, ec); - }, - "number_val failed"); - } - template - expected try_number_val() const noexcept { - return detail::triple_api_expected( - [this](T_Number& out, std::error_code& ec) { - return number_val_ec(out, ec); - }); - } - template - 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 , JSON>>> - JSON(std::string k, T&& v) { - key = std::move(k); - assign_json(valueType, val, std::forward(v)); - } - template , JSON>>> - JSON(T&& v) { - assign_json(valueType, val, std::forward(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& arr); - void append(const JSON& json); - void prepend(const JSON& json); - static JSON array(std::string key, const std::vector& arr); - static JSON object(std::string key, const std::vector& children); - static JSON array(const std::vector& arr = {}); - static JSON object(const std::vector& children = {}); - JsonType valueType = Null; - std::string key, val; - std::vector 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; + static std::function + 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 + T_Number get_number(const std::string& key) const { + return detail::triple_api_throwing( + [this, &key](T_Number& out, std::error_code& ec) { + return get_number_ec(key, out, ec); + }, + "get_number failed"); + } + template + expected try_get_number(const std::string& key) const noexcept { + return detail::triple_api_expected( + [this, &key](T_Number& out, std::error_code& ec) { + return get_number_ec(key, out, ec); + }); + } + template + 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 + T_Number number_val() const { + return detail::triple_api_throwing( + [this](T_Number& out, std::error_code& ec) { + return number_val_ec(out, ec); + }, + "number_val failed"); + } + template + expected try_number_val() const noexcept { + return detail::triple_api_expected( + [this](T_Number& out, std::error_code& ec) { + return number_val_ec(out, ec); + }); + } + template + 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 , JSON>>> + JSON(std::string k, T&& v) { + key = std::move(k); + assign_json(valueType, val, std::forward(v)); + } + template , JSON>>> + JSON(T&& v) { + assign_json(valueType, val, std::forward(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& arr); + void append(const JSON& json); + void prepend(const JSON& json); + static JSON array(std::string key, const std::vector& arr); + static JSON object(std::string key, const std::vector& children); + static JSON array(const std::vector& arr = {}); + static JSON object(const std::vector& children = {}); + JsonType valueType = Null; + std::string key, val; + std::vector 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; + 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 void assign_json(JsonType& rt, std::string& rv, T&& v) { - using C = std::decay_t; - if constexpr (std::is_same_v) { - rt = String; - rv = std::forward(v); - } - else if constexpr (std::is_same_v) { - std::stringstream ss; - ss << "0x" << std::setw(sizeof(void*) * 2) << std::setfill('0') << std::hex - << reinterpret_cast(v); - rt = String; - rv = ss.str(); - } - // 处理 char* 类型 - else if constexpr (std::is_same_v) { - rt = String; - rv = std::string(v); - } - else if constexpr (std::is_same_v) { - rt = String; - rv = std::string(v); // 或 val = v; - } - // 处理 wchar_t* 类型 - else if constexpr (std::is_same_v) { - std::wstring ws(v); - rt = String; - rv = std::string(ws.begin(), ws.end()); - } - // 处理 std::wstring 类型 - else if constexpr (std::is_same_v) { - rt = String; - rv = std::string(v.begin(), v.end()); - } - // 处理枚举类型 - else if constexpr (std::is_enum_v) { - rt = String; - rv = std::string(magic_enum::enum_name(v)); - } - // 处理 bool 类型 - else if constexpr (std::is_same_v) { - rt = Bool; - rv = v ? "true" : "false"; // 对 bool 类型进行自定义转换 - } - // 处理 std::nullptr_t 类型 - else if constexpr (std::is_same_v) { - rt = Null; - rv = "null"; - } - else if constexpr (std::is_arithmetic_v) { - rt = Number; - rv = std::to_string(v); - } - else if constexpr (is_atomic_v) { - assign_json(rt, rv, v.load()); - } - else if constexpr (is_optional_v) { - if (v.has_value()) { - assign_json(rt, rv, v.value()); - } - else { - rt = Null; - rv = "null"; - } - } - else { - rt = String; - rv = v.to_string(); - } + using C = std::decay_t; + if constexpr (std::is_same_v) { + rt = String; + rv = std::forward(v); + } + else if constexpr (std::is_same_v) { + std::stringstream ss; + ss << "0x" << std::setw(sizeof(void*) * 2) << std::setfill('0') << std::hex + << reinterpret_cast(v); + rt = String; + rv = ss.str(); + } + // 处理 char* 类型 + else if constexpr (std::is_same_v) { + rt = String; + rv = std::string(v); + } + else if constexpr (std::is_same_v) { + rt = String; + rv = std::string(v); // 或 val = v; + } + // 处理 wchar_t* 类型 + else if constexpr (std::is_same_v) { + std::wstring ws(v); + rt = String; + rv = std::string(ws.begin(), ws.end()); + } + // 处理 std::wstring 类型 + else if constexpr (std::is_same_v) { + rt = String; + rv = std::string(v.begin(), v.end()); + } + // 处理枚举类型 + else if constexpr (std::is_enum_v) { + rt = String; + rv = std::string(magic_enum::enum_name(v)); + } + // 处理 bool 类型 + else if constexpr (std::is_same_v) { + rt = Bool; + rv = v ? "true" : "false"; // 对 bool 类型进行自定义转换 + } + // 处理 std::nullptr_t 类型 + else if constexpr (std::is_same_v) { + rt = Null; + rv = "null"; + } + else if constexpr (std::is_arithmetic_v) { + rt = Number; + rv = std::to_string(v); + } + else if constexpr (is_atomic_v || is_copyable_atomic_v) { + assign_json(rt, rv, v.load()); + } + else if constexpr (is_optional_v) { + 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); - - + const Psc::JSON *that_json, const char *name); template -bool assign_field_ec(T& field, const Psc::JSON* that_json, const char* name, std::error_code& ec) noexcept; +bool assign_field_ec(T& field, const Psc::JSON *that_json, const char *name, std::error_code& ec) noexcept; template -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"); +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 expected 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); - }); + 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 -bool assign_field_ec(T& field, const Psc::JSON* that_json, const char* name, +bool assign_field_ec(T& field, const Psc::JSON *that_json, const char *name, std::error_code& ec) noexcept { - using C = clean_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) { - return j->bool_val_ec(field, ec); - } - else if constexpr (std::is_arithmetic_v && !std::is_same_v) { - return that_json->get_number_ec(name, field, ec); - } - else if constexpr (std::is_same_v) { - return that_json->get_string_ec(name, field, ec); - } - else if constexpr (std::is_enum_v) { - std::string value; - if (!that_json->get_string_ec(name, value, ec)) - return false; - field = Psc::to_enum(value); - } - else if constexpr (is_atomic_v) { - using V = atomic_value_t; - V value{}; - if constexpr (std::is_same_v) { - if (!that_json->get_bool_ec(name, value, ec)) - return false; - } - else if constexpr (std::is_arithmetic_v) { - if (!that_json->get_number_ec(name, value, ec)) - return false; - } - field = value; - } - else if constexpr (is_copyable_atomic_v) { - using V = copyable_atomic_value_t; - V value{}; - if constexpr (std::is_same_v) { - if (!that_json->get_bool_ec(name, value, ec)) { - return false; - } - } - else if constexpr (std::is_arithmetic_v) { - if (!that_json->get_number_ec(name, value, ec)) { - return false; - } - } - field = value; - } - else if constexpr (is_optional_v) { - using V = optional_value_t; - V value{}; - if constexpr (std::is_same_v) { - if (!that_json->get_bool_ec(name, value, ec)) - return false; - } - else if constexpr (std::is_arithmetic_v && !std::is_same_v) { - if (!that_json->get_number_ec(name, value, ec)) - return false; - } - else if constexpr (std::is_same_v) { - if (!that_json->get_string_ec(name, value, ec)) - return false; - } - else if constexpr (std::is_enum_v) { - std::string text; - if (!that_json->get_string_ec(name, text, ec)) - return false; - value = Psc::to_enum(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; + using C = clean_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) { + return j->bool_val_ec(field, ec); + } + else if constexpr (std::is_arithmetic_v && !std::is_same_v) { + return that_json->get_number_ec(name, field, ec); + } + else if constexpr (std::is_same_v) { + return that_json->get_string_ec(name, field, ec); + } + else if constexpr (std::is_enum_v) { + std::string value; + if (!that_json->get_string_ec(name, value, ec)) return false; + field = Psc::to_enum(value); + } + else if constexpr (is_atomic_v) { + using V = atomic_value_t; + V value{}; + if constexpr (std::is_same_v) { + if (!that_json->get_bool_ec(name, value, ec)) return false; + } + else if constexpr (std::is_arithmetic_v) { + if (!that_json->get_number_ec(name, value, ec)) return false; + } + field = value; + } + else if constexpr (is_copyable_atomic_v) { + using V = copyable_atomic_value_t; + V value{}; + if constexpr (std::is_same_v) { + if (!that_json->get_bool_ec(name, value, ec)) { + return false; + } + } + else if constexpr (std::is_arithmetic_v) { + if (!that_json->get_number_ec(name, value, ec)) { + return false; + } + } + field = value; + } + else if constexpr (is_optional_v) { + using V = optional_value_t; + V value{}; + if constexpr (std::is_same_v) { + if (!that_json->get_bool_ec(name, value, ec)) return false; + } + else if constexpr (std::is_arithmetic_v && !std::is_same_v) { + if (!that_json->get_number_ec(name, value, ec)) return false; + } + else if constexpr (std::is_same_v) { + if (!that_json->get_string_ec(name, value, ec)) return false; + } + else if constexpr (std::is_enum_v) { + std::string text; + if (!that_json->get_string_ec(name, text, ec)) return false; + value = Psc::to_enum(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 -void output_field(const T& field, Psc::JSON& ret, const char* name) { - using C = clean_t; - // bool - if constexpr (std::is_same_v) { - ret.append(Psc::JSON(name, field)); - } - // 数字(排除 bool) - else if constexpr (std::is_arithmetic_v && !std::is_same_v) { - ret.append(Psc::JSON(name, field)); - } - // string - else if constexpr (std::is_same_v) { - ret.append(Psc::JSON(name, field)); - } - // enum - else if constexpr (std::is_enum_v) { - ret.append(Psc::JSON(name, Psc::to_string(field))); - } - // atomic - else if constexpr (is_atomic_v) { - ret.append(Psc::JSON(name, field.load())); - } - else if constexpr (is_copyable_atomic_v) { - ret.append(Psc::JSON(name, field.load())); - } - // optional - else if constexpr (is_optional_v) { - using V = optional_value_t; - if (field.has_value()) { - // --- optional - if constexpr (std::is_same_v) { - ret.append(Psc::JSON(name, field.value())); - } - // --- optional - else if constexpr (std::is_arithmetic_v && !std::is_same_v) { - ret.append(Psc::JSON(name, field.value())); - } - // --- optional - else if constexpr (std::is_same_v) { - ret.append(Psc::JSON(name, field.value())); - } - // --- optional - else if constexpr (std::is_enum_v) { - ret.append(Psc::JSON(name, to_string(field.value()))); - } - // --- optional - else if constexpr (is_atomic_v) { - 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) { - 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 output_field(const T& field, Psc::JSON& ret, const char *name) { + using C = clean_t; + // bool + if constexpr (std::is_same_v) { + ret.append(Psc::JSON(name, field)); + } + // 数字(排除 bool) + else if constexpr (std::is_arithmetic_v && !std::is_same_v) { + ret.append(Psc::JSON(name, field)); + } + // string + else if constexpr (std::is_same_v) { + ret.append(Psc::JSON(name, field)); + } + // enum + else if constexpr (std::is_enum_v) { + ret.append(Psc::JSON(name, Psc::to_string(field))); + } + // atomic + else if constexpr (is_atomic_v || is_copyable_atomic_v) { + ret.append(Psc::JSON(name, field.load())); + } + // optional + else if constexpr (is_optional_v) { + using V = optional_value_t; + if (field.has_value()) { + // --- optional + if constexpr (std::is_same_v) { + ret.append(Psc::JSON(name, field.value())); + } + // --- optional + else if constexpr (std::is_arithmetic_v && !std::is_same_v) { + ret.append(Psc::JSON(name, field.value())); + } + // --- optional + else if constexpr (std::is_same_v) { + ret.append(Psc::JSON(name, field.value())); + } + // --- optional + else if constexpr (std::is_enum_v) { + ret.append(Psc::JSON(name, to_string(field.value()))); + } + // --- optional + else if constexpr (is_atomic_v || is_copyable_atomic_v) { + 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) { + 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) \ @@ -523,11 +507,9 @@ void output_field(const T& field, Psc::JSON& ret, const char* name) { 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 #include - #define PSC_TO_JSON \ [[nodiscard]] Psc::JSON to_base_json() const { \ Psc::JSON ret = Psc::JSON::object(); \ @@ -537,7 +519,6 @@ void output_field(const T& field, Psc::JSON& ret, const char* name) { }); \ return ret; \ } - #define PSC_FROM_JSON \ void from_base_json(const Psc::JSON *that_json) { \ using T = std::remove_cv_t>; \ @@ -548,7 +529,6 @@ void from_base_json(const Psc::JSON *that_json) { \ Psc::check_json_assign_field(t, ec, that_json, name); \ }); \ } - #define PSC_USE_JSON \ PSC_TO_JSON \ -PSC_FROM_JSON \ No newline at end of file +PSC_FROM_JSON