#include "stdafx.h" #include "RuntimeJson.h" #include #include #include #include #include #include namespace { class JsonParser { public: JsonParser(const std::string& text, std::string& error) : mText(text), mError(error), mPosition(0) { } bool parse(JsonValue& value) { skipWhitespace(); if (!parseValue(value)) return false; skipWhitespace(); if (mPosition != mText.size()) { setError("Unexpected trailing characters in JSON input."); return false; } return true; } private: bool parseValue(JsonValue& value) { if (mPosition >= mText.size()) { setError("Unexpected end of JSON input."); return false; } char ch = mText[mPosition]; if (ch == '{') return parseObject(value); if (ch == '[') return parseArray(value); if (ch == '"') { std::string stringValue; if (!parseString(stringValue)) return false; value = JsonValue(stringValue); return true; } if (ch == 't') return parseLiteral("true", JsonValue(true), value); if (ch == 'f') return parseLiteral("false", JsonValue(false), value); if (ch == 'n') return parseLiteral("null", JsonValue(), value); if (ch == '-' || std::isdigit(static_cast(ch))) return parseNumber(value); setError("Unexpected token while parsing JSON."); return false; } bool parseObject(JsonValue& value) { value = JsonValue::MakeObject(); ++mPosition; skipWhitespace(); if (consume('}')) return true; while (mPosition < mText.size()) { std::string key; if (!parseString(key)) return false; skipWhitespace(); if (!consume(':')) { setError("Expected ':' after JSON object key."); return false; } skipWhitespace(); JsonValue item; if (!parseValue(item)) return false; value.set(key, item); skipWhitespace(); if (consume('}')) return true; if (!consume(',')) { setError("Expected ',' or '}' in JSON object."); return false; } skipWhitespace(); } setError("Unexpected end of JSON object."); return false; } bool parseArray(JsonValue& value) { value = JsonValue::MakeArray(); ++mPosition; skipWhitespace(); if (consume(']')) return true; while (mPosition < mText.size()) { JsonValue item; if (!parseValue(item)) return false; value.pushBack(item); skipWhitespace(); if (consume(']')) return true; if (!consume(',')) { setError("Expected ',' or ']' in JSON array."); return false; } skipWhitespace(); } setError("Unexpected end of JSON array."); return false; } bool parseString(std::string& value) { if (!consume('"')) { setError("Expected string literal."); return false; } std::ostringstream result; while (mPosition < mText.size()) { char ch = mText[mPosition++]; if (ch == '"') { value = result.str(); return true; } if (ch == '\\') { if (mPosition >= mText.size()) { setError("Unexpected end of escaped JSON string."); return false; } char escaped = mText[mPosition++]; switch (escaped) { case '"': result << '"'; break; case '\\': result << '\\'; break; case '/': result << '/'; break; case 'b': result << '\b'; break; case 'f': result << '\f'; break; case 'n': result << '\n'; break; case 'r': result << '\r'; break; case 't': result << '\t'; break; case 'u': setError("Unicode escape sequences are not supported in this JSON parser."); return false; default: setError("Invalid escape sequence in JSON string."); return false; } } else { result << ch; } } setError("Unexpected end of JSON string."); return false; } bool parseNumber(JsonValue& value) { std::size_t start = mPosition; if (mText[mPosition] == '-') ++mPosition; while (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) ++mPosition; if (mPosition < mText.size() && mText[mPosition] == '.') { ++mPosition; while (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) ++mPosition; } if (mPosition < mText.size() && (mText[mPosition] == 'e' || mText[mPosition] == 'E')) { ++mPosition; if (mPosition < mText.size() && (mText[mPosition] == '+' || mText[mPosition] == '-')) ++mPosition; while (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) ++mPosition; } std::string token = mText.substr(start, mPosition - start); char* endPtr = nullptr; double parsed = strtod(token.c_str(), &endPtr); if (endPtr == token.c_str() || *endPtr != '\0') { setError("Invalid JSON number."); return false; } value = JsonValue(parsed); return true; } bool parseLiteral(const char* literal, const JsonValue& literalValue, JsonValue& value) { std::size_t length = strlen(literal); if (mText.compare(mPosition, length, literal) != 0) { setError("Invalid JSON literal."); return false; } mPosition += length; value = literalValue; return true; } void skipWhitespace() { while (mPosition < mText.size() && std::isspace(static_cast(mText[mPosition]))) ++mPosition; } bool consume(char expected) { if (mPosition < mText.size() && mText[mPosition] == expected) { ++mPosition; return true; } return false; } void setError(const std::string& error) { if (mError.empty()) mError = error; } const std::string& mText; std::string& mError; std::size_t mPosition; }; void SerializeJsonImpl(const JsonValue& value, std::ostringstream& output, bool pretty, int indentLevel) { auto indent = [&](int level) { if (!pretty) return; for (int i = 0; i < level; ++i) output << " "; }; switch (value.type()) { case JsonValue::Type::Null: output << "null"; break; case JsonValue::Type::Boolean: output << (value.asBoolean() ? "true" : "false"); break; case JsonValue::Type::Number: { double number = value.asNumber(); if (std::isfinite(number)) { output << std::setprecision(15) << number; } else { output << "0"; } break; } case JsonValue::Type::String: { output << '"'; for (char ch : value.asString()) { switch (ch) { case '"': output << "\\\""; break; case '\\': output << "\\\\"; break; case '\b': output << "\\b"; break; case '\f': output << "\\f"; break; case '\n': output << "\\n"; break; case '\r': output << "\\r"; break; case '\t': output << "\\t"; break; default: output << ch; break; } } output << '"'; break; } case JsonValue::Type::Array: { output << "["; const std::vector& array = value.asArray(); if (!array.empty()) { if (pretty) output << "\n"; for (std::size_t i = 0; i < array.size(); ++i) { indent(indentLevel + 1); SerializeJsonImpl(array[i], output, pretty, indentLevel + 1); if (i + 1 != array.size()) output << ","; if (pretty) output << "\n"; } indent(indentLevel); } output << "]"; break; } case JsonValue::Type::Object: { output << "{"; const std::map& object = value.asObject(); if (!object.empty()) { if (pretty) output << "\n"; std::size_t index = 0; for (const auto& item : object) { indent(indentLevel + 1); SerializeJsonImpl(JsonValue(item.first), output, pretty, indentLevel + 1); output << (pretty ? ": " : ":"); SerializeJsonImpl(item.second, output, pretty, indentLevel + 1); if (++index != object.size()) output << ","; if (pretty) output << "\n"; } indent(indentLevel); } output << "}"; break; } } } } JsonValue::JsonValue() : mType(Type::Null), mBooleanValue(false), mNumberValue(0.0) { } JsonValue::JsonValue(bool value) : mType(Type::Boolean), mBooleanValue(value), mNumberValue(0.0) { } JsonValue::JsonValue(double value) : mType(Type::Number), mBooleanValue(false), mNumberValue(value) { } JsonValue::JsonValue(const char* value) : mType(Type::String), mBooleanValue(false), mNumberValue(0.0), mStringValue(value ? value : "") { } JsonValue::JsonValue(const std::string& value) : mType(Type::String), mBooleanValue(false), mNumberValue(0.0), mStringValue(value) { } JsonValue JsonValue::MakeArray() { JsonValue value; value.mType = Type::Array; return value; } JsonValue JsonValue::MakeObject() { JsonValue value; value.mType = Type::Object; return value; } bool JsonValue::asBoolean(bool fallback) const { return mType == Type::Boolean ? mBooleanValue : fallback; } double JsonValue::asNumber(double fallback) const { return mType == Type::Number ? mNumberValue : fallback; } const std::string& JsonValue::asString() const { static const std::string emptyString; return mType == Type::String ? mStringValue : emptyString; } const std::vector& JsonValue::asArray() const { static const std::vector emptyArray; return mType == Type::Array ? mArrayValue : emptyArray; } const std::map& JsonValue::asObject() const { static const std::map emptyObject; return mType == Type::Object ? mObjectValue : emptyObject; } std::vector& JsonValue::array() { if (mType != Type::Array) { mType = Type::Array; mArrayValue.clear(); } return mArrayValue; } std::map& JsonValue::object() { if (mType != Type::Object) { mType = Type::Object; mObjectValue.clear(); } return mObjectValue; } void JsonValue::pushBack(const JsonValue& value) { array().push_back(value); } void JsonValue::set(const std::string& key, const JsonValue& value) { object()[key] = value; } const JsonValue* JsonValue::find(const std::string& key) const { if (mType != Type::Object) return nullptr; auto iterator = mObjectValue.find(key); return iterator != mObjectValue.end() ? &iterator->second : nullptr; } bool ParseJson(const std::string& text, JsonValue& value, std::string& error) { error.clear(); JsonParser parser(text, error); return parser.parse(value); } std::string SerializeJson(const JsonValue& value, bool pretty) { std::ostringstream output; SerializeJsonImpl(value, output, pretty, 0); return output.str(); }