Created
January 15, 2026 09:31
-
-
Save aneury1/aaa1c14ba50f7ea8d0d18fa78807a79c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <string> | |
| #include <map> | |
| #include <vector> | |
| #include <sstream> | |
| #include <stdexcept> | |
| #include <memory> | |
| #include <cctype> | |
| // Forward declaration | |
| class JsonValue; | |
| // Type definitions | |
| using JsonObject = std::map<std::string, JsonValue>; | |
| using JsonArray = std::vector<JsonValue>; | |
| // JSON Value class | |
| class JsonValue { | |
| public: | |
| enum Type { NULL_TYPE, BOOL, NUMBER, STRING, ARRAY, OBJECT }; | |
| private: | |
| Type type_; | |
| union { | |
| bool bool_val; | |
| double num_val; | |
| }; | |
| std::string str_val; | |
| std::shared_ptr<JsonArray> arr_val; | |
| std::shared_ptr<JsonObject> obj_val; | |
| public: | |
| // Constructors | |
| JsonValue() : type_(NULL_TYPE), num_val(0) {} | |
| JsonValue(bool b) : type_(BOOL), bool_val(b) {} | |
| JsonValue(int n) : type_(NUMBER), num_val(n) {} | |
| JsonValue(double n) : type_(NUMBER), num_val(n) {} | |
| JsonValue(const std::string& s) : type_(STRING), str_val(s) {} | |
| JsonValue(const char* s) : type_(STRING), str_val(s) {} | |
| JsonValue(const JsonArray& arr) : type_(ARRAY), arr_val(std::make_shared<JsonArray>(arr)) {} | |
| JsonValue(const JsonObject& obj) : type_(OBJECT), obj_val(std::make_shared<JsonObject>(obj)) {} | |
| // Copy constructor | |
| JsonValue(const JsonValue& other) : type_(other.type_) { | |
| switch (type_) { | |
| case BOOL: bool_val = other.bool_val; break; | |
| case NUMBER: num_val = other.num_val; break; | |
| case STRING: str_val = other.str_val; break; | |
| case ARRAY: arr_val = other.arr_val; break; | |
| case OBJECT: obj_val = other.obj_val; break; | |
| default: break; | |
| } | |
| } | |
| // Assignment operator | |
| JsonValue& operator=(const JsonValue& other) { | |
| if (this != &other) { | |
| type_ = other.type_; | |
| switch (type_) { | |
| case BOOL: bool_val = other.bool_val; break; | |
| case NUMBER: num_val = other.num_val; break; | |
| case STRING: str_val = other.str_val; break; | |
| case ARRAY: arr_val = other.arr_val; break; | |
| case OBJECT: obj_val = other.obj_val; break; | |
| default: break; | |
| } | |
| } | |
| return *this; | |
| } | |
| // Type checking | |
| Type getType() const { return type_; } | |
| bool isNull() const { return type_ == NULL_TYPE; } | |
| bool isBool() const { return type_ == BOOL; } | |
| bool isNumber() const { return type_ == NUMBER; } | |
| bool isString() const { return type_ == STRING; } | |
| bool isArray() const { return type_ == ARRAY; } | |
| bool isObject() const { return type_ == OBJECT; } | |
| // Getters | |
| bool asBool() const { | |
| if (type_ != BOOL) throw std::runtime_error("Not a boolean"); | |
| return bool_val; | |
| } | |
| double asNumber() const { | |
| if (type_ != NUMBER) throw std::runtime_error("Not a number"); | |
| return num_val; | |
| } | |
| const std::string& asString() const { | |
| if (type_ != STRING) throw std::runtime_error("Not a string"); | |
| return str_val; | |
| } | |
| JsonArray& asArray() { | |
| if (type_ != ARRAY) throw std::runtime_error("Not an array"); | |
| return *arr_val; | |
| } | |
| const JsonArray& asArray() const { | |
| if (type_ != ARRAY) throw std::runtime_error("Not an array"); | |
| return *arr_val; | |
| } | |
| JsonObject& asObject() { | |
| if (type_ != OBJECT) throw std::runtime_error("Not an object"); | |
| return *obj_val; | |
| } | |
| const JsonObject& asObject() const { | |
| if (type_ != OBJECT) throw std::runtime_error("Not an object"); | |
| return *obj_val; | |
| } | |
| // Array access | |
| JsonValue& operator[](size_t index) { | |
| return asArray()[index]; | |
| } | |
| const JsonValue& operator[](size_t index) const { | |
| return asArray()[index]; | |
| } | |
| // Object access | |
| JsonValue& operator[](const std::string& key) { | |
| if (type_ == NULL_TYPE) { | |
| type_ = OBJECT; | |
| obj_val = std::make_shared<JsonObject>(); | |
| } | |
| return (*obj_val)[key]; | |
| } | |
| // Serialize to string | |
| std::string stringify(int indent = -1, int currentIndent = 0) const { | |
| std::ostringstream oss; | |
| std::string indentStr = (indent >= 0) ? std::string(currentIndent, ' ') : ""; | |
| std::string newline = (indent >= 0) ? "\n" : ""; | |
| switch (type_) { | |
| case NULL_TYPE: | |
| oss << "null"; | |
| break; | |
| case BOOL: | |
| oss << (bool_val ? "true" : "false"); | |
| break; | |
| case NUMBER: | |
| if (num_val == static_cast<int>(num_val)) { | |
| oss << static_cast<int>(num_val); | |
| } else { | |
| oss << num_val; | |
| } | |
| break; | |
| case STRING: | |
| oss << "\"" << escapeString(str_val) << "\""; | |
| break; | |
| case ARRAY: | |
| oss << "["; | |
| if (!arr_val->empty()) { | |
| if (indent >= 0) oss << newline; | |
| for (size_t i = 0; i < arr_val->size(); ++i) { | |
| if (indent >= 0) oss << std::string(currentIndent + indent, ' '); | |
| oss << (*arr_val)[i].stringify(indent, currentIndent + indent); | |
| if (i < arr_val->size() - 1) oss << ","; | |
| if (indent >= 0) oss << newline; | |
| } | |
| if (indent >= 0) oss << indentStr; | |
| } | |
| oss << "]"; | |
| break; | |
| case OBJECT: | |
| oss << "{"; | |
| if (!obj_val->empty()) { | |
| if (indent >= 0) oss << newline; | |
| size_t i = 0; | |
| for (const auto& pair : *obj_val) { | |
| if (indent >= 0) oss << std::string(currentIndent + indent, ' '); | |
| oss << "\"" << escapeString(pair.first) << "\":"; | |
| if (indent >= 0) oss << " "; | |
| oss << pair.second.stringify(indent, currentIndent + indent); | |
| if (++i < obj_val->size()) oss << ","; | |
| if (indent >= 0) oss << newline; | |
| } | |
| if (indent >= 0) oss << indentStr; | |
| } | |
| oss << "}"; | |
| break; | |
| } | |
| return oss.str(); | |
| } | |
| private: | |
| static std::string escapeString(const std::string& s) { | |
| std::ostringstream oss; | |
| for (char c : s) { | |
| switch (c) { | |
| case '\"': oss << "\\\""; break; | |
| case '\\': oss << "\\\\"; break; | |
| case '\b': oss << "\\b"; break; | |
| case '\f': oss << "\\f"; break; | |
| case '\n': oss << "\\n"; break; | |
| case '\r': oss << "\\r"; break; | |
| case '\t': oss << "\\t"; break; | |
| default: | |
| if (c < 32) { | |
| oss << "\\u" << std::hex << std::setfill('0') << std::setw(4) << static_cast<int>(c); | |
| } else { | |
| oss << c; | |
| } | |
| } | |
| } | |
| return oss.str(); | |
| } | |
| }; | |
| // JSON Parser | |
| class JsonParser { | |
| private: | |
| std::string json_; | |
| size_t pos_; | |
| void skipWhitespace() { | |
| while (pos_ < json_.size() && std::isspace(json_[pos_])) { | |
| ++pos_; | |
| } | |
| } | |
| char peek() const { | |
| return pos_ < json_.size() ? json_[pos_] : '\0'; | |
| } | |
| char get() { | |
| return pos_ < json_.size() ? json_[pos_++] : '\0'; | |
| } | |
| void expect(char c) { | |
| if (get() != c) { | |
| throw std::runtime_error(std::string("Expected '") + c + "'"); | |
| } | |
| } | |
| JsonValue parseValue() { | |
| skipWhitespace(); | |
| char c = peek(); | |
| if (c == 'n') return parseNull(); | |
| if (c == 't' || c == 'f') return parseBool(); | |
| if (c == '\"') return parseString(); | |
| if (c == '[') return parseArray(); | |
| if (c == '{') return parseObject(); | |
| if (c == '-' || std::isdigit(c)) return parseNumber(); | |
| throw std::runtime_error("Unexpected character"); | |
| } | |
| JsonValue parseNull() { | |
| if (json_.substr(pos_, 4) == "null") { | |
| pos_ += 4; | |
| return JsonValue(); | |
| } | |
| throw std::runtime_error("Invalid null value"); | |
| } | |
| JsonValue parseBool() { | |
| if (json_.substr(pos_, 4) == "true") { | |
| pos_ += 4; | |
| return JsonValue(true); | |
| } | |
| if (json_.substr(pos_, 5) == "false") { | |
| pos_ += 5; | |
| return JsonValue(false); | |
| } | |
| throw std::runtime_error("Invalid boolean value"); | |
| } | |
| JsonValue parseNumber() { | |
| size_t start = pos_; | |
| if (peek() == '-') ++pos_; | |
| while (std::isdigit(peek())) ++pos_; | |
| if (peek() == '.') { | |
| ++pos_; | |
| while (std::isdigit(peek())) ++pos_; | |
| } | |
| if (peek() == 'e' || peek() == 'E') { | |
| ++pos_; | |
| if (peek() == '+' || peek() == '-') ++pos_; | |
| while (std::isdigit(peek())) ++pos_; | |
| } | |
| std::string numStr = json_.substr(start, pos_ - start); | |
| return JsonValue(std::stod(numStr)); | |
| } | |
| JsonValue parseString() { | |
| expect('\"'); | |
| std::ostringstream oss; | |
| while (peek() != '\"' && peek() != '\0') { | |
| char c = get(); | |
| if (c == '\\') { | |
| c = get(); | |
| switch (c) { | |
| case '\"': oss << '\"'; break; | |
| case '\\': oss << '\\'; break; | |
| case '/': oss << '/'; break; | |
| case 'b': oss << '\b'; break; | |
| case 'f': oss << '\f'; break; | |
| case 'n': oss << '\n'; break; | |
| case 'r': oss << '\r'; break; | |
| case 't': oss << '\t'; break; | |
| case 'u': { | |
| // Unicode escape (simplified) | |
| std::string hex = json_.substr(pos_, 4); | |
| pos_ += 4; | |
| oss << static_cast<char>(std::stoi(hex, nullptr, 16)); | |
| break; | |
| } | |
| default: oss << c; | |
| } | |
| } else { | |
| oss << c; | |
| } | |
| } | |
| expect('\"'); | |
| return JsonValue(oss.str()); | |
| } | |
| JsonValue parseArray() { | |
| expect('['); | |
| JsonArray arr; | |
| skipWhitespace(); | |
| if (peek() == ']') { | |
| ++pos_; | |
| return JsonValue(arr); | |
| } | |
| while (true) { | |
| arr.push_back(parseValue()); | |
| skipWhitespace(); | |
| if (peek() == ']') { | |
| ++pos_; | |
| break; | |
| } | |
| expect(','); | |
| } | |
| return JsonValue(arr); | |
| } | |
| JsonValue parseObject() { | |
| expect('{'); | |
| JsonObject obj; | |
| skipWhitespace(); | |
| if (peek() == '}') { | |
| ++pos_; | |
| return JsonValue(obj); | |
| } | |
| while (true) { | |
| skipWhitespace(); | |
| JsonValue key = parseString(); | |
| skipWhitespace(); | |
| expect(':'); | |
| JsonValue value = parseValue(); | |
| obj[key.asString()] = value; | |
| skipWhitespace(); | |
| if (peek() == '}') { | |
| ++pos_; | |
| break; | |
| } | |
| expect(','); | |
| } | |
| return JsonValue(obj); | |
| } | |
| public: | |
| static JsonValue parse(const std::string& json) { | |
| JsonParser parser; | |
| parser.json_ = json; | |
| parser.pos_ = 0; | |
| return parser.parseValue(); | |
| } | |
| }; | |
| // Example usage | |
| int main() { | |
| try { | |
| // Building JSON | |
| std::cout << "=== Building JSON ===" << std::endl; | |
| JsonValue root; | |
| root["name"] = "John Doe"; | |
| root["age"] = 30; | |
| root["isStudent"] = false; | |
| root["address"]["street"] = "123 Main St"; | |
| root["address"]["city"] = "New York"; | |
| JsonArray hobbies; | |
| hobbies.push_back(JsonValue("reading")); | |
| hobbies.push_back(JsonValue("coding")); | |
| hobbies.push_back(JsonValue("gaming")); | |
| root["hobbies"] = hobbies; | |
| std::string jsonStr = root.stringify(2); | |
| std::cout << jsonStr << std::endl << std::endl; | |
| // Parsing JSON | |
| std::cout << "=== Parsing JSON ===" << std::endl; | |
| std::string input = R"({ | |
| "name": "Jane Smith", | |
| "age": 25, | |
| "scores": [95, 87, 92], | |
| "active": true, | |
| "metadata": null | |
| })"; | |
| JsonValue parsed = JsonParser::parse(input); | |
| std::cout << "Name: " << parsed["name"].asString() << std::endl; | |
| std::cout << "Age: " << parsed["age"].asNumber() << std::endl; | |
| std::cout << "First score: " << parsed["scores"][0].asNumber() << std::endl; | |
| std::cout << "Active: " << (parsed["active"].asBool() ? "true" : "false") << std::endl; | |
| std::cout << "\nReparsed JSON:" << std::endl; | |
| std::cout << parsed.stringify(2) << std::endl; | |
| } catch (const std::exception& e) { | |
| std::cerr << "Error: " << e.what() << std::endl; | |
| return 1; | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment