#include "stdafx.h" #include "RuntimeJson.h" #include #include #include #include #include #include #include namespace { int HexDigitValue(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; return -1; } bool IsHighSurrogate(unsigned int codePoint) { return codePoint >= 0xD800 && codePoint <= 0xDBFF; } bool IsLowSurrogate(unsigned int codePoint) { return codePoint >= 0xDC00 && codePoint <= 0xDFFF; } void AppendUtf8(unsigned int codePoint, std::ostringstream& output) { if (codePoint <= 0x7F) { output << static_cast(codePoint); } else if (codePoint <= 0x7FF) { output << static_cast(0xC0 | ((codePoint >> 6) & 0x1F)); output << static_cast(0x80 | (codePoint & 0x3F)); } else if (codePoint <= 0xFFFF) { output << static_cast(0xE0 | ((codePoint >> 12) & 0x0F)); output << static_cast(0x80 | ((codePoint >> 6) & 0x3F)); output << static_cast(0x80 | (codePoint & 0x3F)); } else { output << static_cast(0xF0 | ((codePoint >> 18) & 0x07)); output << static_cast(0x80 | ((codePoint >> 12) & 0x3F)); output << static_cast(0x80 | ((codePoint >> 6) & 0x3F)); output << static_cast(0x80 | (codePoint & 0x3F)); } } void AppendControlEscape(unsigned char ch, std::ostringstream& output) { const char* digits = "0123456789ABCDEF"; output << "\\u00" << digits[(ch >> 4) & 0x0F] << digits[ch & 0x0F]; } 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': if (!parseUnicodeEscape(result)) return false; break; default: setError("Invalid escape sequence in JSON string."); return false; } } else { if (static_cast(ch) < 0x20) { setError("Unescaped control character in JSON string."); return false; } result << ch; } } setError("Unexpected end of JSON string."); return false; } bool parseHexCodePoint(unsigned int& codePoint) { if (mPosition + 4 > mText.size()) { setError("Unexpected end of Unicode escape sequence."); return false; } codePoint = 0; for (int i = 0; i < 4; ++i) { const int digit = HexDigitValue(mText[mPosition + i]); if (digit < 0) { setError("Invalid Unicode escape sequence in JSON string."); return false; } codePoint = (codePoint << 4) | static_cast(digit); } mPosition += 4; return true; } bool parseUnicodeEscape(std::ostringstream& result) { unsigned int codePoint = 0; if (!parseHexCodePoint(codePoint)) return false; if (IsHighSurrogate(codePoint)) { if (mPosition + 2 > mText.size() || mText[mPosition] != '\\' || mText[mPosition + 1] != 'u') { setError("High surrogate Unicode escape must be followed by a low surrogate."); return false; } mPosition += 2; unsigned int lowSurrogate = 0; if (!parseHexCodePoint(lowSurrogate)) return false; if (!IsLowSurrogate(lowSurrogate)) { setError("High surrogate Unicode escape must be followed by a low surrogate."); return false; } codePoint = 0x10000 + (((codePoint - 0xD800) << 10) | (lowSurrogate - 0xDC00)); } else if (IsLowSurrogate(codePoint)) { setError("Low surrogate Unicode escape without preceding high surrogate."); return false; } AppendUtf8(codePoint, result); return true; } bool parseNumber(JsonValue& value) { std::size_t start = mPosition; if (mText[mPosition] == '-') ++mPosition; if (mPosition >= mText.size()) { setError("Invalid JSON number."); return false; } if (mText[mPosition] == '0') { ++mPosition; if (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) { setError("JSON numbers must not contain leading zeroes."); return false; } } else if (mText[mPosition] >= '1' && mText[mPosition] <= '9') { while (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) ++mPosition; } else { setError("Invalid JSON number."); return false; } if (mPosition < mText.size() && mText[mPosition] == '.') { ++mPosition; if (mPosition >= mText.size() || !std::isdigit(static_cast(mText[mPosition]))) { setError("JSON number fraction must contain at least one digit."); return false; } 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; if (mPosition >= mText.size() || !std::isdigit(static_cast(mText[mPosition]))) { setError("JSON number exponent must contain at least one digit."); return false; } while (mPosition < mText.size() && std::isdigit(static_cast(mText[mPosition]))) ++mPosition; } std::string token = mText.substr(start, mPosition - start); char* endPtr = nullptr; errno = 0; double parsed = strtod(token.c_str(), &endPtr); if (endPtr == token.c_str() || *endPtr != '\0' || errno == ERANGE || !std::isfinite(parsed)) { 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: if (static_cast(ch) < 0x20) AppendControlEscape(static_cast(ch), output); else 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.reset(Type::Array); return value; } JsonValue JsonValue::MakeObject() { JsonValue value; value.reset(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) reset(Type::Array); return mArrayValue; } std::map& JsonValue::object() { if (mType != Type::Object) reset(Type::Object); 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; } void JsonValue::reset(Type type) { mType = type; mBooleanValue = false; mNumberValue = 0.0; mStringValue.clear(); mArrayValue.clear(); mObjectValue.clear(); } 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(); }