ed4a561704
- Add complete API documentation for application integration (docs/API_DOCUMENT.md) - Fix check-update API to use version ID comparison instead of string comparison - Add validation: client version must exist in server version list - Add support for patch/incremental updates with base_version check - Add C++ SDK with HTTP client, JSON parser, and crypto support - Add simple_test.cpp for standalone testing on Windows Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
354 lines
12 KiB
C++
354 lines
12 KiB
C++
// 简化版JSON库 - 仅包含测试所需功能
|
|
#ifndef SIMPLE_JSON_HPP
|
|
#define SIMPLE_JSON_HPP
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <cstdint>
|
|
#include <cmath>
|
|
|
|
namespace simple {
|
|
|
|
class json {
|
|
public:
|
|
enum class type_t {
|
|
null,
|
|
boolean,
|
|
number,
|
|
string,
|
|
array,
|
|
object
|
|
};
|
|
|
|
json() : type_(type_t::null) {}
|
|
json(std::nullptr_t) : type_(type_t::null) {}
|
|
json(bool b) : type_(type_t::boolean), bool_val_(b) {}
|
|
json(int n) : type_(type_t::number), num_val_(n) {}
|
|
json(int64_t n) : type_(type_t::number), num_val_(static_cast<double>(n)) {}
|
|
json(uint64_t n) : type_(type_t::number), num_val_(static_cast<double>(n)) {}
|
|
json(double n) : type_(type_t::number), num_val_(n) {}
|
|
json(const char* s) : type_(type_t::string), str_val_(s) {}
|
|
json(const std::string& s) : type_(type_t::string), str_val_(s) {}
|
|
|
|
type_t type() const { return type_; }
|
|
bool is_null() const { return type_ == type_t::null; }
|
|
bool is_bool() const { return type_ == type_t::boolean; }
|
|
bool is_number() const { return type_ == type_t::number; }
|
|
bool is_string() const { return type_ == type_t::string; }
|
|
bool is_array() const { return type_ == type_t::array; }
|
|
bool is_object() const { return type_ == type_t::object; }
|
|
|
|
size_t size() const {
|
|
if (type_ == type_t::array) return arr_val_.size();
|
|
if (type_ == type_t::object) return obj_val_.size();
|
|
return 0;
|
|
}
|
|
|
|
// Array access
|
|
json& operator[](size_t idx) {
|
|
if (type_ != type_t::array) {
|
|
type_ = type_t::array;
|
|
arr_val_.clear();
|
|
}
|
|
if (idx >= arr_val_.size()) arr_val_.resize(idx + 1);
|
|
return arr_val_[idx];
|
|
}
|
|
|
|
const json& operator[](size_t idx) const {
|
|
static json null_val;
|
|
if (type_ != type_t::array || idx >= arr_val_.size()) return null_val;
|
|
return arr_val_[idx];
|
|
}
|
|
|
|
// Object access
|
|
json& operator[](const std::string& key) {
|
|
if (type_ != type_t::object) {
|
|
type_ = type_t::object;
|
|
obj_val_.clear();
|
|
}
|
|
return obj_val_[key];
|
|
}
|
|
|
|
const json& operator[](const std::string& key) const {
|
|
static json null_val;
|
|
if (type_ != type_t::object) return null_val;
|
|
auto it = obj_val_.find(key);
|
|
return (it != obj_val_.end()) ? it->second : null_val;
|
|
}
|
|
|
|
// const char* overload to avoid ambiguity
|
|
json& operator[](const char* key) {
|
|
return (*this)[std::string(key)];
|
|
}
|
|
|
|
const json& operator[](const char* key) const {
|
|
return (*this)[std::string(key)];
|
|
}
|
|
|
|
bool contains(const std::string& key) const {
|
|
return type_ == type_t::object && obj_val_.find(key) != obj_val_.end();
|
|
}
|
|
|
|
// Value getters
|
|
bool get_bool() const { return bool_val_; }
|
|
double get_double() const { return num_val_; }
|
|
int64_t get_int64() const { return static_cast<int64_t>(num_val_); }
|
|
uint64_t get_uint64() const { return static_cast<uint64_t>(num_val_); }
|
|
int get_int() const { return static_cast<int>(num_val_); }
|
|
const std::string& get_string() const { return str_val_; }
|
|
|
|
// Implicit conversions
|
|
operator bool() const { return type_ == type_t::boolean ? bool_val_ : false; }
|
|
operator int() const { return static_cast<int>(num_val_); }
|
|
operator int64_t() const { return static_cast<int64_t>(num_val_); }
|
|
operator uint64_t() const { return static_cast<uint64_t>(num_val_); }
|
|
operator double() const { return num_val_; }
|
|
operator std::string() const { return str_val_; }
|
|
|
|
// Push back for arrays
|
|
void push_back(const json& val) {
|
|
if (type_ != type_t::array) {
|
|
type_ = type_t::array;
|
|
arr_val_.clear();
|
|
}
|
|
arr_val_.push_back(val);
|
|
}
|
|
|
|
// Serialization
|
|
std::string dump(int indent = -1) const {
|
|
std::ostringstream ss;
|
|
dump_impl(ss, indent >= 0 ? indent : -1, 0);
|
|
return ss.str();
|
|
}
|
|
|
|
static json parse(const std::string& str) {
|
|
size_t pos = 0;
|
|
return parse_impl(str, pos);
|
|
}
|
|
|
|
static json array() {
|
|
json j;
|
|
j.type_ = type_t::array;
|
|
return j;
|
|
}
|
|
|
|
static json object() {
|
|
json j;
|
|
j.type_ = type_t::object;
|
|
return j;
|
|
}
|
|
|
|
// Iterator support for arrays
|
|
std::vector<json>::iterator begin() { return arr_val_.begin(); }
|
|
std::vector<json>::iterator end() { return arr_val_.end(); }
|
|
std::vector<json>::const_iterator begin() const { return arr_val_.begin(); }
|
|
std::vector<json>::const_iterator end() const { return arr_val_.end(); }
|
|
|
|
// Get object items
|
|
std::map<std::string, json>::const_iterator obj_begin() const { return obj_val_.begin(); }
|
|
std::map<std::string, json>::const_iterator obj_end() const { return obj_val_.end(); }
|
|
|
|
private:
|
|
type_t type_ = type_t::null;
|
|
bool bool_val_ = false;
|
|
double num_val_ = 0.0;
|
|
std::string str_val_;
|
|
std::vector<json> arr_val_;
|
|
std::map<std::string, json> obj_val_;
|
|
|
|
void dump_impl(std::ostringstream& ss, int indent, int level) const {
|
|
switch (type_) {
|
|
case type_t::null:
|
|
ss << "null";
|
|
break;
|
|
case type_t::boolean:
|
|
ss << (bool_val_ ? "true" : "false");
|
|
break;
|
|
case type_t::number:
|
|
if (num_val_ == static_cast<int64_t>(num_val_)) {
|
|
ss << static_cast<int64_t>(num_val_);
|
|
} else {
|
|
ss << num_val_;
|
|
}
|
|
break;
|
|
case type_t::string:
|
|
ss << '"';
|
|
for (char c : str_val_) {
|
|
switch (c) {
|
|
case '"': ss << "\\\""; break;
|
|
case '\\': ss << "\\\\"; break;
|
|
case '\n': ss << "\\n"; break;
|
|
case '\r': ss << "\\r"; break;
|
|
case '\t': ss << "\\t"; break;
|
|
default: ss << c;
|
|
}
|
|
}
|
|
ss << '"';
|
|
break;
|
|
case type_t::array:
|
|
ss << '[';
|
|
if (indent >= 0) {
|
|
ss << '\n';
|
|
for (int i = 0; i <= level; ++i) ss << std::string(indent, ' ');
|
|
}
|
|
for (size_t i = 0; i < arr_val_.size(); ++i) {
|
|
if (i > 0) {
|
|
ss << ',';
|
|
if (indent >= 0) ss << '\n' << std::string((level + 1) * indent, ' ');
|
|
}
|
|
arr_val_[i].dump_impl(ss, indent, level + 1);
|
|
}
|
|
if (indent >= 0) ss << '\n' << std::string(level * indent, ' ');
|
|
ss << ']';
|
|
break;
|
|
case type_t::object:
|
|
ss << '{';
|
|
if (indent >= 0) ss << '\n' << std::string((level + 1) * indent, ' ');
|
|
bool first = true;
|
|
for (const auto& p : obj_val_) {
|
|
if (!first) {
|
|
ss << ',';
|
|
if (indent >= 0) ss << '\n' << std::string((level + 1) * indent, ' ');
|
|
}
|
|
first = false;
|
|
ss << '"' << p.first << "\":";
|
|
if (indent >= 0) ss << ' ';
|
|
p.second.dump_impl(ss, indent, level + 1);
|
|
}
|
|
if (indent >= 0) ss << '\n' << std::string(level * indent, ' ');
|
|
ss << '}';
|
|
break;
|
|
}
|
|
}
|
|
|
|
static json parse_impl(const std::string& str, size_t& pos) {
|
|
skip_whitespace(str, pos);
|
|
if (pos >= str.size()) throw std::runtime_error("Unexpected end of input");
|
|
|
|
char c = str[pos];
|
|
if (c == 'n') {
|
|
pos += 4;
|
|
return json();
|
|
} else if (c == 't') {
|
|
pos += 4;
|
|
return json(true);
|
|
} else if (c == 'f') {
|
|
pos += 5;
|
|
return json(false);
|
|
} else if (c == '"') {
|
|
return json(parse_string(str, pos));
|
|
} else if (c == '[') {
|
|
return parse_array(str, pos);
|
|
} else if (c == '{') {
|
|
return parse_object(str, pos);
|
|
} else if (c == '-' || std::isdigit(c)) {
|
|
return parse_number(str, pos);
|
|
}
|
|
throw std::runtime_error(std::string("Unexpected character: ") + c);
|
|
}
|
|
|
|
static void skip_whitespace(const std::string& str, size_t& pos) {
|
|
while (pos < str.size() && std::isspace(str[pos])) ++pos;
|
|
}
|
|
|
|
static std::string parse_string(const std::string& str, size_t& pos) {
|
|
++pos; // skip opening quote
|
|
std::string result;
|
|
while (pos < str.size() && str[pos] != '"') {
|
|
if (str[pos] == '\\' && pos + 1 < str.size()) {
|
|
++pos;
|
|
switch (str[pos]) {
|
|
case '"': result += '"'; break;
|
|
case '\\': result += '\\'; break;
|
|
case 'n': result += '\n'; break;
|
|
case 'r': result += '\r'; break;
|
|
case 't': result += '\t'; break;
|
|
default: result += str[pos];
|
|
}
|
|
} else {
|
|
result += str[pos];
|
|
}
|
|
++pos;
|
|
}
|
|
if (pos < str.size()) ++pos; // skip closing quote
|
|
return result;
|
|
}
|
|
|
|
static json parse_number(const std::string& str, size_t& pos) {
|
|
size_t start = pos;
|
|
if (str[pos] == '-') ++pos;
|
|
while (pos < str.size() && std::isdigit(str[pos])) ++pos;
|
|
if (pos < str.size() && str[pos] == '.') {
|
|
++pos;
|
|
while (pos < str.size() && std::isdigit(str[pos])) ++pos;
|
|
}
|
|
if (pos < str.size() && (str[pos] == 'e' || str[pos] == 'E')) {
|
|
++pos;
|
|
if (pos < str.size() && (str[pos] == '+' || str[pos] == '-')) ++pos;
|
|
while (pos < str.size() && std::isdigit(str[pos])) ++pos;
|
|
}
|
|
return json(std::stod(str.substr(start, pos - start)));
|
|
}
|
|
|
|
static json parse_array(const std::string& str, size_t& pos) {
|
|
json arr = json::array();
|
|
++pos; // skip '['
|
|
skip_whitespace(str, pos);
|
|
if (pos < str.size() && str[pos] == ']') {
|
|
++pos;
|
|
return arr;
|
|
}
|
|
while (pos < str.size()) {
|
|
arr.push_back(parse_impl(str, pos));
|
|
skip_whitespace(str, pos);
|
|
if (pos >= str.size() || str[pos] == ']') {
|
|
++pos;
|
|
break;
|
|
}
|
|
if (str[pos] == ',') ++pos;
|
|
skip_whitespace(str, pos);
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
static json parse_object(const std::string& str, size_t& pos) {
|
|
json obj = json::object();
|
|
++pos; // skip '{'
|
|
skip_whitespace(str, pos);
|
|
if (pos < str.size() && str[pos] == '}') {
|
|
++pos;
|
|
return obj;
|
|
}
|
|
while (pos < str.size()) {
|
|
skip_whitespace(str, pos);
|
|
if (str[pos] != '"') throw std::runtime_error("Expected string key");
|
|
std::string key = parse_string(str, pos);
|
|
skip_whitespace(str, pos);
|
|
if (pos >= str.size() || str[pos] != ':') throw std::runtime_error("Expected ':'");
|
|
++pos;
|
|
obj[key] = parse_impl(str, pos);
|
|
skip_whitespace(str, pos);
|
|
if (pos >= str.size() || str[pos] == '}') {
|
|
++pos;
|
|
break;
|
|
}
|
|
if (str[pos] == ',') ++pos;
|
|
}
|
|
return obj;
|
|
}
|
|
};
|
|
|
|
// Type alias for compatibility
|
|
using nlohmann_json = json;
|
|
|
|
} // namespace simple
|
|
|
|
namespace nlohmann {
|
|
using json = simple::json;
|
|
}
|
|
|
|
#endif // SIMPLE_JSON_HPP
|