Skip to content

Instantly share code, notes, and snippets.

@aneury1
Created January 15, 2026 09:31
Show Gist options
  • Select an option

  • Save aneury1/aaa1c14ba50f7ea8d0d18fa78807a79c to your computer and use it in GitHub Desktop.

Select an option

Save aneury1/aaa1c14ba50f7ea8d0d18fa78807a79c to your computer and use it in GitHub Desktop.
#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