Files
SpineParticlesWeb/connector/native/json.cpp
T
2026-09-05 23:35:45 +08:00

319 lines
11 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.
#include "json.hpp"
#include <cmath>
#include <cstdlib>
#include <iomanip>
#include <limits>
#include <sstream>
#include <stdexcept>
namespace connector {
namespace {
void append_utf8(std::string& out, unsigned codepoint) {
if (codepoint <= 0x7f) {
out.push_back(static_cast<char>(codepoint));
} else if (codepoint <= 0x7ff) {
out.push_back(static_cast<char>(0xc0 | (codepoint >> 6)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
} else if (codepoint <= 0xffff) {
out.push_back(static_cast<char>(0xe0 | (codepoint >> 12)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
} else {
out.push_back(static_cast<char>(0xf0 | (codepoint >> 18)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
out.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
out.push_back(static_cast<char>(0x80 | (codepoint & 0x3f)));
}
}
class Parser {
public:
explicit Parser(const std::string& text) : text_(text) {}
Json parse() {
skip_space();
Json result = value();
skip_space();
if (position_ != text_.size()) fail("JSON 末尾存在多余内容");
return result;
}
private:
const std::string& text_;
std::size_t position_ = 0;
[[noreturn]] void fail(const std::string& message) const {
throw std::runtime_error(message + "(位置 " + std::to_string(position_) + "");
}
void skip_space() {
while (position_ < text_.size()) {
const char c = text_[position_];
if (c != ' ' && c != '\t' && c != '\r' && c != '\n') break;
++position_;
}
}
bool consume(char expected) {
skip_space();
if (position_ >= text_.size() || text_[position_] != expected) return false;
++position_;
return true;
}
void literal(const char* expected) {
while (*expected) {
if (position_ >= text_.size() || text_[position_++] != *expected++) fail("JSON 字面量无效");
}
}
unsigned hex4() {
unsigned value = 0;
for (int index = 0; index < 4; ++index) {
if (position_ >= text_.size()) fail("JSON Unicode 转义不完整");
const char c = text_[position_++];
value <<= 4;
if (c >= '0' && c <= '9') value |= static_cast<unsigned>(c - '0');
else if (c >= 'a' && c <= 'f') value |= static_cast<unsigned>(c - 'a' + 10);
else if (c >= 'A' && c <= 'F') value |= static_cast<unsigned>(c - 'A' + 10);
else fail("JSON Unicode 转义无效");
}
return value;
}
std::string string_value() {
if (!consume('"')) fail("JSON 字符串缺少引号");
std::string out;
while (position_ < text_.size()) {
const char c = text_[position_++];
if (c == '"') return out;
if (static_cast<unsigned char>(c) < 0x20) fail("JSON 字符串包含控制字符");
if (c != '\\') {
out.push_back(c);
continue;
}
if (position_ >= text_.size()) fail("JSON 转义不完整");
switch (text_[position_++]) {
case '"': out.push_back('"'); break;
case '\\': out.push_back('\\'); break;
case '/': out.push_back('/'); break;
case 'b': out.push_back('\b'); break;
case 'f': out.push_back('\f'); break;
case 'n': out.push_back('\n'); break;
case 'r': out.push_back('\r'); break;
case 't': out.push_back('\t'); break;
case 'u': {
unsigned codepoint = hex4();
if (codepoint >= 0xd800 && codepoint <= 0xdbff) {
if (position_ + 2 > text_.size() || text_[position_++] != '\\' || text_[position_++] != 'u') {
fail("JSON Unicode 代理对无效");
}
const unsigned low = hex4();
if (low < 0xdc00 || low > 0xdfff) fail("JSON Unicode 代理对无效");
codepoint = 0x10000 + ((codepoint - 0xd800) << 10) + (low - 0xdc00);
}
append_utf8(out, codepoint);
break;
}
default: fail("JSON 转义字符无效");
}
}
fail("JSON 字符串未结束");
}
Json number_value() {
const std::size_t start = position_;
if (position_ < text_.size() && text_[position_] == '-') ++position_;
if (position_ >= text_.size()) fail("JSON 数字无效");
if (text_[position_] == '0') {
++position_;
} else {
if (text_[position_] < '1' || text_[position_] > '9') fail("JSON 数字无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
if (position_ < text_.size() && text_[position_] == '.') {
++position_;
if (position_ >= text_.size() || text_[position_] < '0' || text_[position_] > '9') fail("JSON 小数无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
if (position_ < text_.size() && (text_[position_] == 'e' || text_[position_] == 'E')) {
++position_;
if (position_ < text_.size() && (text_[position_] == '+' || text_[position_] == '-')) ++position_;
if (position_ >= text_.size() || text_[position_] < '0' || text_[position_] > '9') fail("JSON 指数无效");
while (position_ < text_.size() && text_[position_] >= '0' && text_[position_] <= '9') ++position_;
}
const std::string token = text_.substr(start, position_ - start);
char* end = nullptr;
const double parsed = std::strtod(token.c_str(), &end);
if (!end || *end || !std::isfinite(parsed)) fail("JSON 数字超出范围");
return Json(parsed);
}
Json array_value() {
consume('[');
Json::Array values;
if (consume(']')) return Json(std::move(values));
while (true) {
values.push_back(value());
if (consume(']')) break;
if (!consume(',')) fail("JSON 数组缺少逗号");
}
return Json(std::move(values));
}
Json object_value() {
consume('{');
Json::Object values;
if (consume('}')) return Json(std::move(values));
while (true) {
skip_space();
if (position_ >= text_.size() || text_[position_] != '"') fail("JSON 对象键名无效");
const std::string key = string_value();
if (!consume(':')) fail("JSON 对象缺少冒号");
values[key] = value();
if (consume('}')) break;
if (!consume(',')) fail("JSON 对象缺少逗号");
}
return Json(std::move(values));
}
Json value() {
skip_space();
if (position_ >= text_.size()) fail("JSON 内容不完整");
switch (text_[position_]) {
case 'n': literal("null"); return Json();
case 't': literal("true"); return Json(true);
case 'f': literal("false"); return Json(false);
case '"': return Json(string_value());
case '[': return array_value();
case '{': return object_value();
default: return number_value();
}
}
};
std::string indent_text(int indent, int depth) {
return indent > 0 ? std::string(static_cast<std::size_t>(indent * depth), ' ') : std::string();
}
void dump_string(std::ostringstream& out, const std::string& value) {
out << '"';
for (const unsigned char c : value) {
switch (c) {
case '"': out << "\\\""; break;
case '\\': out << "\\\\"; break;
case '\b': out << "\\b"; break;
case '\f': out << "\\f"; break;
case '\n': out << "\\n"; break;
case '\r': out << "\\r"; break;
case '\t': out << "\\t"; break;
default:
if (c < 0x20) {
out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << static_cast<int>(c)
<< std::dec << std::setfill(' ');
} else {
out << static_cast<char>(c);
}
}
}
out << '"';
}
void dump_value(std::ostringstream& out, const Json& value, int indent, int depth) {
if (value.is_null()) {
out << "null";
} else if (value.is_bool()) {
out << (value.boolean() ? "true" : "false");
} else if (value.is_number()) {
out << std::setprecision(std::numeric_limits<double>::max_digits10) << value.number();
} else if (value.is_string()) {
dump_string(out, value.string());
} else if (value.is_array()) {
out << '[';
const auto& values = value.array();
for (std::size_t index = 0; index < values.size(); ++index) {
if (index) out << ',';
if (indent > 0) out << '\n' << indent_text(indent, depth + 1);
dump_value(out, values[index], indent, depth + 1);
}
if (indent > 0 && !values.empty()) out << '\n' << indent_text(indent, depth);
out << ']';
} else {
out << '{';
const auto& values = value.object();
std::size_t index = 0;
for (const auto& [key, child] : values) {
if (index++) out << ',';
if (indent > 0) out << '\n' << indent_text(indent, depth + 1);
dump_string(out, key);
out << (indent > 0 ? ": " : ":");
dump_value(out, child, indent, depth + 1);
}
if (indent > 0 && !values.empty()) out << '\n' << indent_text(indent, depth);
out << '}';
}
}
} // namespace
Json::Json() : value_(nullptr) {}
Json::Json(std::nullptr_t) : value_(nullptr) {}
Json::Json(bool value) : value_(value) {}
Json::Json(double value) : value_(value) {}
Json::Json(const char* value) : value_(std::string(value ? value : "")) {}
Json::Json(std::string value) : value_(std::move(value)) {}
Json::Json(Array value) : value_(std::move(value)) {}
Json::Json(Object value) : value_(std::move(value)) {}
Json Json::parse(const std::string& text) { return Parser(text).parse(); }
std::string Json::dump(int indent) const {
std::ostringstream out;
dump_value(out, *this, indent, 0);
return out.str();
}
bool Json::is_null() const { return std::holds_alternative<std::nullptr_t>(value_); }
bool Json::is_bool() const { return std::holds_alternative<bool>(value_); }
bool Json::is_number() const { return std::holds_alternative<double>(value_); }
bool Json::is_string() const { return std::holds_alternative<std::string>(value_); }
bool Json::is_array() const { return std::holds_alternative<Array>(value_); }
bool Json::is_object() const { return std::holds_alternative<Object>(value_); }
bool Json::boolean(bool fallback) const { return is_bool() ? std::get<bool>(value_) : fallback; }
double Json::number(double fallback) const { return is_number() ? std::get<double>(value_) : fallback; }
const std::string& Json::string() const {
static const std::string empty;
return is_string() ? std::get<std::string>(value_) : empty;
}
const Json::Array& Json::array() const {
static const Array empty;
return is_array() ? std::get<Array>(value_) : empty;
}
Json::Array& Json::array() {
if (!is_array()) value_ = Array{};
return std::get<Array>(value_);
}
const Json::Object& Json::object() const {
static const Object empty;
return is_object() ? std::get<Object>(value_) : empty;
}
Json::Object& Json::object() {
if (!is_object()) value_ = Object{};
return std::get<Object>(value_);
}
const Json* Json::find(const std::string& key) const {
if (!is_object()) return nullptr;
const auto found = object().find(key);
return found == object().end() ? nullptr : &found->second;
}
Json* Json::find(const std::string& key) {
if (!is_object()) return nullptr;
const auto found = object().find(key);
return found == object().end() ? nullptr : &found->second;
}
Json& Json::operator[](const std::string& key) { return object()[key]; }
} // namespace connector