nlohmann/json (Validation)

Validation LibraryC++JSONSchema ValidationnlohmannModern C++

Library

nlohmann/json (Validation)

Overview

nlohmann/json is the most popular JSON library for Modern C++. It provides an intuitive API similar to the standard library, making JSON reading and writing straightforward. Validation functionality is achieved through powerful JSON schema validation by combining with third-party libraries (such as json-schema-validator or valijson). It supports C++11 and later, can be easily integrated as a header-only library, and allows handling JSON data with the same feel as STL containers.

Details

The nlohmann/json library is a modern C++ library for parsing, generating, and manipulating JSON. It provides an intuitive API similar to STL containers, allowing JSON objects to be handled naturally as C++ values. It supports basic type checking, structural validation, and implementation of custom validation logic as validation features. Through integration with external libraries, JSON Schema Draft 4/7 compliant schema validation is also possible. It offers an excellent balance of performance and usability and is widely adopted in enterprise-level production environments.

Key Features

  • Intuitive API: Natural operation feel like STL containers
  • Type Safety: Strict type checking and error handling
  • Schema Validation: Advanced validation through combination with external libraries
  • High Performance: Optimized JSON parsing and generation engine
  • Header-only: Easy integration and distribution
  • Rich Features: Custom serializers, iterators, patch operations, and more

Pros and Cons

Pros

  • Intuitive and easy-to-use API similar to C++ standard library
  • Excellent performance and efficient memory usage
  • Rich documentation and active community support
  • Proven track record with adoption by many companies
  • Flexible customization features and extensibility
  • Comprehensive error handling and debugging support

Cons

  • Increased compilation time (large header file)
  • Built-in schema validation functionality is limited
  • External libraries required for complex validation
  • High memory usage when processing large JSON files
  • Learning curve exists (especially for advanced features)

References

Code Examples

Installation and Basic Setup

# Install using vcpkg
vcpkg install nlohmann-json

# Install using Conan
conan install nlohmann_json/3.11.2@

# CMake find_package usage example
find_package(nlohmann_json REQUIRED)
target_link_libraries(your_target nlohmann_json::nlohmann_json)

# Without package manager
wget https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp
# Place json.hpp in appropriate directory

Basic JSON Operations and Validation

#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>

using json = nlohmann::json;

// Basic validation functionality
class JSONValidator {
public:
    // JSON type checking
    static bool isString(const json& j) {
        return j.is_string();
    }
    
    static bool isNumber(const json& j) {
        return j.is_number();
    }
    
    static bool isBoolean(const json& j) {
        return j.is_boolean();
    }
    
    static bool isArray(const json& j) {
        return j.is_array();
    }
    
    static bool isObject(const json& j) {
        return j.is_object();
    }
    
    // Check required fields
    static bool hasRequiredFields(const json& j, const std::vector<std::string>& fields) {
        if (!j.is_object()) {
            return false;
        }
        
        for (const auto& field : fields) {
            if (!j.contains(field)) {
                return false;
            }
        }
        
        return true;
    }
    
    // Value range check
    static bool isInRange(const json& j, double min_val, double max_val) {
        if (!j.is_number()) {
            return false;
        }
        
        double value = j.get<double>();
        return value >= min_val && value <= max_val;
    }
    
    // String length check
    static bool isStringLengthValid(const json& j, size_t min_len, size_t max_len) {
        if (!j.is_string()) {
            return false;
        }
        
        std::string str = j.get<std::string>();
        return str.length() >= min_len && str.length() <= max_len;
    }
    
    // Array size check
    static bool isArraySizeValid(const json& j, size_t min_size, size_t max_size) {
        if (!j.is_array()) {
            return false;
        }
        
        return j.size() >= min_size && j.size() <= max_size;
    }
};

int main() {
    try {
        // Basic JSON creation and validation
        {
            json person = {
                {"name", "John Doe"},
                {"age", 30},
                {"email", "[email protected]"},
                {"skills", {"C++", "Python", "JavaScript"}},
                {"active", true}
            };
            
            std::cout << "Person JSON:\n" << person.dump(4) << std::endl;
            
            // Basic validation
            if (JSONValidator::hasRequiredFields(person, {"name", "age", "email"})) {
                std::cout << "✓ All required fields present" << std::endl;
            }
            
            if (JSONValidator::isStringLengthValid(person["name"], 2, 50)) {
                std::cout << "✓ Name length is valid" << std::endl;
            }
            
            if (JSONValidator::isInRange(person["age"], 0, 120)) {
                std::cout << "✓ Age is within valid range" << std::endl;
            }
            
            if (JSONValidator::isArraySizeValid(person["skills"], 1, 10)) {
                std::cout << "✓ Skills array size is valid" << std::endl;
            }
        }
        
        // JSON file loading and validation
        {
            // Read JSON file
            std::ifstream config_file("config.json");
            if (config_file.is_open()) {
                json config;
                config_file >> config;
                
                // Configuration file validation
                std::vector<std::string> required_config = {"app_name", "port", "debug_mode"};
                if (JSONValidator::hasRequiredFields(config, required_config)) {
                    std::cout << "✓ Configuration file is valid" << std::endl;
                } else {
                    std::cout << "✗ Configuration file missing required fields" << std::endl;
                }
                
                config_file.close();
            }
        }
        
        // Error handling example
        {
            json invalid_json = {
                {"name", 123},  // Number instead of expected string
                {"age", "thirty"},  // String instead of expected number
                {"email", ""}  // Empty string
            };
            
            // Type checking
            if (!JSONValidator::isString(invalid_json["name"])) {
                std::cout << "✗ Name must be a string" << std::endl;
            }
            
            if (!JSONValidator::isNumber(invalid_json["age"])) {
                std::cout << "✗ Age must be a number" << std::endl;
            }
            
            if (!JSONValidator::isStringLengthValid(invalid_json["email"], 5, 100)) {
                std::cout << "✗ Email address length is invalid" << std::endl;
            }
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSON error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

Advanced Validation Features and Custom Verification

#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <functional>
#include <map>
#include <optional>

using json = nlohmann::json;

// Validation result
struct ValidationResult {
    bool is_valid;
    std::vector<std::string> errors;
    
    ValidationResult() : is_valid(true) {}
    
    void addError(const std::string& error) {
        errors.push_back(error);
        is_valid = false;
    }
    
    void merge(const ValidationResult& other) {
        if (!other.is_valid) {
            is_valid = false;
            errors.insert(errors.end(), other.errors.begin(), other.errors.end());
        }
    }
};

// Advanced validator
class AdvancedJSONValidator {
private:
    // Email address validation
    static bool isValidEmail(const std::string& email) {
        static const std::regex email_regex(
            R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)"
        );
        return std::regex_match(email, email_regex);
    }
    
    // URL validation
    static bool isValidURL(const std::string& url) {
        static const std::regex url_regex(
            R"(^https?://[^\s/$.?#].[^\s]*$)"
        );
        return std::regex_match(url, url_regex);
    }
    
    // Date validation (ISO 8601 format)
    static bool isValidDate(const std::string& date) {
        static const std::regex date_regex(
            R"(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?$)"
        );
        return std::regex_match(date, date_regex);
    }

public:
    // Advanced string field validation
    static ValidationResult validateStringField(const json& j, const std::string& field_name,
                                              bool required = true,
                                              size_t min_length = 0,
                                              size_t max_length = std::numeric_limits<size_t>::max(),
                                              const std::string& pattern = "",
                                              const std::string& format = "") {
        ValidationResult result;
        
        // Check field existence
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("Required field '" + field_name + "' not found");
            }
            return result;
        }
        
        // Type check
        if (!j[field_name].is_string()) {
            result.addError("Field '" + field_name + "' must be a string");
            return result;
        }
        
        std::string value = j[field_name].get<std::string>();
        
        // Length check
        if (value.length() < min_length || value.length() > max_length) {
            result.addError("Field '" + field_name + "' length must be between " + 
                          std::to_string(min_length) + " and " + 
                          std::to_string(max_length) + " characters");
        }
        
        // Pattern check
        if (!pattern.empty()) {
            std::regex regex_pattern(pattern);
            if (!std::regex_match(value, regex_pattern)) {
                result.addError("Field '" + field_name + "' does not match pattern");
            }
        }
        
        // Format check
        if (!format.empty()) {
            if (format == "email" && !isValidEmail(value)) {
                result.addError("Field '" + field_name + "' is not a valid email address");
            } else if (format == "url" && !isValidURL(value)) {
                result.addError("Field '" + field_name + "' is not a valid URL");
            } else if (format == "date" && !isValidDate(value)) {
                result.addError("Field '" + field_name + "' is not a valid date format");
            }
        }
        
        return result;
    }
    
    // Number field validation
    static ValidationResult validateNumberField(const json& j, const std::string& field_name,
                                              bool required = true,
                                              double min_value = std::numeric_limits<double>::lowest(),
                                              double max_value = std::numeric_limits<double>::max(),
                                              bool integer_only = false) {
        ValidationResult result;
        
        // Check field existence
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("Required field '" + field_name + "' not found");
            }
            return result;
        }
        
        // Type check
        if (!j[field_name].is_number()) {
            result.addError("Field '" + field_name + "' must be a number");
            return result;
        }
        
        double value = j[field_name].get<double>();
        
        // Range check
        if (value < min_value || value > max_value) {
            result.addError("Field '" + field_name + "' value must be between " + 
                          std::to_string(min_value) + " and " + 
                          std::to_string(max_value));
        }
        
        // Integer check
        if (integer_only && std::floor(value) != value) {
            result.addError("Field '" + field_name + "' must be an integer");
        }
        
        return result;
    }
    
    // Array field validation
    static ValidationResult validateArrayField(const json& j, const std::string& field_name,
                                             bool required = true,
                                             size_t min_items = 0,
                                             size_t max_items = std::numeric_limits<size_t>::max(),
                                             std::function<ValidationResult(const json&, size_t)> item_validator = nullptr) {
        ValidationResult result;
        
        // Check field existence
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("Required field '" + field_name + "' not found");
            }
            return result;
        }
        
        // Type check
        if (!j[field_name].is_array()) {
            result.addError("Field '" + field_name + "' must be an array");
            return result;
        }
        
        const json& array = j[field_name];
        
        // Size check
        if (array.size() < min_items || array.size() > max_items) {
            result.addError("Field '" + field_name + "' must have between " + 
                          std::to_string(min_items) + " and " + 
                          std::to_string(max_items) + " items");
        }
        
        // Validate each item
        if (item_validator) {
            for (size_t i = 0; i < array.size(); ++i) {
                ValidationResult item_result = item_validator(array[i], i);
                if (!item_result.is_valid) {
                    for (const auto& error : item_result.errors) {
                        result.addError("Array '" + field_name + "' element[" + 
                                      std::to_string(i) + "]: " + error);
                    }
                }
            }
        }
        
        return result;
    }
    
    // Object field validation
    static ValidationResult validateObjectField(const json& j, const std::string& field_name,
                                              bool required = true,
                                              std::function<ValidationResult(const json&)> object_validator = nullptr) {
        ValidationResult result;
        
        // Check field existence
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("Required field '" + field_name + "' not found");
            }
            return result;
        }
        
        // Type check
        if (!j[field_name].is_object()) {
            result.addError("Field '" + field_name + "' must be an object");
            return result;
        }
        
        // Object validation
        if (object_validator) {
            ValidationResult obj_result = object_validator(j[field_name]);
            if (!obj_result.is_valid) {
                for (const auto& error : obj_result.errors) {
                    result.addError("Object '" + field_name + "': " + error);
                }
            }
        }
        
        return result;
    }
};

// Concrete schema validation example
class UserProfileValidator {
public:
    static ValidationResult validateUserProfile(const json& user_profile) {
        ValidationResult result;
        
        // Basic information validation
        auto name_result = AdvancedJSONValidator::validateStringField(
            user_profile, "name", true, 2, 50
        );
        result.merge(name_result);
        
        auto email_result = AdvancedJSONValidator::validateStringField(
            user_profile, "email", true, 5, 100, "", "email"
        );
        result.merge(email_result);
        
        auto age_result = AdvancedJSONValidator::validateNumberField(
            user_profile, "age", true, 0, 120, true
        );
        result.merge(age_result);
        
        // Skills validation
        auto skills_result = AdvancedJSONValidator::validateArrayField(
            user_profile, "skills", false, 0, 20,
            [](const json& item, size_t index) -> ValidationResult {
                ValidationResult item_result;
                if (!item.is_string()) {
                    item_result.addError("Skill must be a string");
                } else {
                    std::string skill = item.get<std::string>();
                    if (skill.empty() || skill.length() > 30) {
                        item_result.addError("Skill must be between 1 and 30 characters");
                    }
                }
                return item_result;
            }
        );
        result.merge(skills_result);
        
        // Contact information validation
        auto contact_result = AdvancedJSONValidator::validateObjectField(
            user_profile, "contact", false,
            [](const json& contact) -> ValidationResult {
                ValidationResult contact_result;
                
                // Phone number validation
                if (contact.contains("phone")) {
                    auto phone_result = AdvancedJSONValidator::validateStringField(
                        contact, "phone", false, 10, 15, R"(^\+?[1-9]\d{1,14}$)"
                    );
                    contact_result.merge(phone_result);
                }
                
                // Address validation
                if (contact.contains("address")) {
                    auto address_result = AdvancedJSONValidator::validateStringField(
                        contact, "address", false, 10, 200
                    );
                    contact_result.merge(address_result);
                }
                
                // Website validation
                if (contact.contains("website")) {
                    auto website_result = AdvancedJSONValidator::validateStringField(
                        contact, "website", false, 0, 200, "", "url"
                    );
                    contact_result.merge(website_result);
                }
                
                return contact_result;
            }
        );
        result.merge(contact_result);
        
        return result;
    }
};

int main() {
    try {
        // Test valid user profile
        {
            json valid_profile = {
                {"name", "John Doe"},
                {"email", "[email protected]"},
                {"age", 30},
                {"skills", {"C++", "Python", "JavaScript", "SQL"}},
                {"contact", {
                    {"phone", "+1234567890"},
                    {"address", "123 Main Street, City, State 12345"},
                    {"website", "https://example.com"}
                }}
            };
            
            std::cout << "Valid user profile:\n" << valid_profile.dump(4) << std::endl;
            
            ValidationResult result = UserProfileValidator::validateUserProfile(valid_profile);
            
            if (result.is_valid) {
                std::cout << "✓ User profile is valid" << std::endl;
            } else {
                std::cout << "✗ Validation errors:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        std::cout << "\n" << std::string(50, '-') << "\n\n";
        
        // Test invalid user profile
        {
            json invalid_profile = {
                {"name", "A"},  // Too short name
                {"email", "invalid-email"},  // Invalid email
                {"age", 150},  // Out of range age
                {"skills", {"", "very long skill name that exceeds the maximum length limit"}},  // Invalid skills
                {"contact", {
                    {"phone", "123"},  // Invalid phone number
                    {"address", "Short"},  // Too short address
                    {"website", "not-a-url"}  // Invalid URL
                }}
            };
            
            std::cout << "Invalid user profile:\n" << invalid_profile.dump(4) << std::endl;
            
            ValidationResult result = UserProfileValidator::validateUserProfile(invalid_profile);
            
            if (result.is_valid) {
                std::cout << "✓ User profile is valid" << std::endl;
            } else {
                std::cout << "✗ Validation errors:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSON error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}

JSON Schema Integration and Schema-driven Validation

// Note: This code is an integration example with json-schema-validator library
// Additional libraries are required for actual use

#include <nlohmann/json.hpp>
#include <iostream>
#include <string>
#include <fstream>
#include <map>
#include <vector>

using json = nlohmann::json;

// Schema definition class
class JSONSchemaManager {
private:
    std::map<std::string, json> schemas;
    
public:
    // Define and register schema
    void defineSchema(const std::string& schema_name, const json& schema) {
        schemas[schema_name] = schema;
    }
    
    // Load schema from file
    bool loadSchemaFromFile(const std::string& schema_name, const std::string& file_path) {
        try {
            std::ifstream file(file_path);
            if (!file.is_open()) {
                return false;
            }
            
            json schema;
            file >> schema;
            schemas[schema_name] = schema;
            return true;
        } catch (const std::exception&) {
            return false;
        }
    }
    
    // Get schema
    std::optional<json> getSchema(const std::string& schema_name) {
        auto it = schemas.find(schema_name);
        if (it != schemas.end()) {
            return it->second;
        }
        return std::nullopt;
    }
    
    // Basic schema validation (simplified version)
    ValidationResult validateAgainstSchema(const json& data, const std::string& schema_name) {
        ValidationResult result;
        
        auto schema_opt = getSchema(schema_name);
        if (!schema_opt) {
            result.addError("Schema '" + schema_name + "' not found");
            return result;
        }
        
        json schema = *schema_opt;
        
        // Type check
        if (schema.contains("type")) {
            std::string expected_type = schema["type"];
            if (!checkType(data, expected_type)) {
                result.addError("Type mismatch. Expected type: " + expected_type);
            }
        }
        
        // Property check
        if (schema.contains("properties") && data.is_object()) {
            auto properties = schema["properties"];
            for (auto it = properties.begin(); it != properties.end(); ++it) {
                std::string prop_name = it.key();
                json prop_schema = it.value();
                
                if (data.contains(prop_name)) {
                    auto prop_result = validateProperty(data[prop_name], prop_schema, prop_name);
                    result.merge(prop_result);
                }
            }
        }
        
        // Required properties check
        if (schema.contains("required") && data.is_object()) {
            auto required = schema["required"];
            for (const auto& req_prop : required) {
                std::string prop_name = req_prop;
                if (!data.contains(prop_name)) {
                    result.addError("Required property '" + prop_name + "' not found");
                }
            }
        }
        
        return result;
    }
    
private:
    bool checkType(const json& data, const std::string& expected_type) {
        if (expected_type == "object") return data.is_object();
        if (expected_type == "array") return data.is_array();
        if (expected_type == "string") return data.is_string();
        if (expected_type == "number") return data.is_number();
        if (expected_type == "integer") return data.is_number_integer();
        if (expected_type == "boolean") return data.is_boolean();
        if (expected_type == "null") return data.is_null();
        return false;
    }
    
    ValidationResult validateProperty(const json& value, const json& prop_schema, const std::string& prop_name) {
        ValidationResult result;
        
        // Type check
        if (prop_schema.contains("type")) {
            std::string expected_type = prop_schema["type"];
            if (!checkType(value, expected_type)) {
                result.addError("Property '" + prop_name + "' type mismatch");
            }
        }
        
        // String length check
        if (prop_schema.contains("minLength") && value.is_string()) {
            int min_length = prop_schema["minLength"];
            if (value.get<std::string>().length() < static_cast<size_t>(min_length)) {
                result.addError("Property '" + prop_name + "' length below minimum");
            }
        }
        
        if (prop_schema.contains("maxLength") && value.is_string()) {
            int max_length = prop_schema["maxLength"];
            if (value.get<std::string>().length() > static_cast<size_t>(max_length)) {
                result.addError("Property '" + prop_name + "' length exceeds maximum");
            }
        }
        
        // Number range check
        if (prop_schema.contains("minimum") && value.is_number()) {
            double minimum = prop_schema["minimum"];
            if (value.get<double>() < minimum) {
                result.addError("Property '" + prop_name + "' value below minimum");
            }
        }
        
        if (prop_schema.contains("maximum") && value.is_number()) {
            double maximum = prop_schema["maximum"];
            if (value.get<double>() > maximum) {
                result.addError("Property '" + prop_name + "' value exceeds maximum");
            }
        }
        
        // Array length check
        if (prop_schema.contains("minItems") && value.is_array()) {
            int min_items = prop_schema["minItems"];
            if (value.size() < static_cast<size_t>(min_items)) {
                result.addError("Property '" + prop_name + "' array length below minimum");
            }
        }
        
        if (prop_schema.contains("maxItems") && value.is_array()) {
            int max_items = prop_schema["maxItems"];
            if (value.size() > static_cast<size_t>(max_items)) {
                result.addError("Property '" + prop_name + "' array length exceeds maximum");
            }
        }
        
        return result;
    }
};

// Usage example
int main() {
    try {
        JSONSchemaManager schema_manager;
        
        // User profile schema definition
        json user_schema = {
            {"type", "object"},
            {"properties", {
                {"name", {
                    {"type", "string"},
                    {"minLength", 2},
                    {"maxLength", 50}
                }},
                {"email", {
                    {"type", "string"},
                    {"format", "email"}
                }},
                {"age", {
                    {"type", "integer"},
                    {"minimum", 0},
                    {"maximum", 120}
                }},
                {"skills", {
                    {"type", "array"},
                    {"items", {
                        {"type", "string"},
                        {"minLength", 1},
                        {"maxLength", 30}
                    }},
                    {"minItems", 0},
                    {"maxItems", 20}
                }},
                {"contact", {
                    {"type", "object"},
                    {"properties", {
                        {"phone", {
                            {"type", "string"},
                            {"pattern", "^\\+?[1-9]\\d{1,14}$"}
                        }},
                        {"address", {
                            {"type", "string"},
                            {"minLength", 10},
                            {"maxLength", 200}
                        }},
                        {"website", {
                            {"type", "string"},
                            {"format", "uri"}
                        }}
                    }}
                }}
            }},
            {"required", {"name", "email", "age"}}
        };
        
        schema_manager.defineSchema("user_profile", user_schema);
        
        // Valid data validation
        {
            json valid_data = {
                {"name", "John Doe"},
                {"email", "[email protected]"},
                {"age", 30},
                {"skills", {"C++", "Python", "JavaScript"}},
                {"contact", {
                    {"phone", "+1234567890"},
                    {"address", "123 Main Street, City, State 12345"},
                    {"website", "https://example.com"}
                }}
            };
            
            std::cout << "Valid data validation:\n";
            ValidationResult result = schema_manager.validateAgainstSchema(valid_data, "user_profile");
            
            if (result.is_valid) {
                std::cout << "✓ Data is valid" << std::endl;
            } else {
                std::cout << "✗ Validation errors:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        std::cout << "\n" << std::string(50, '-') << "\n\n";
        
        // Invalid data validation
        {
            json invalid_data = {
                {"name", "A"},  // Too short
                {"email", "invalid-email"},  // Invalid email
                {"age", 150},  // Out of range
                {"skills", {"", "very long skill name that exceeds maximum length"}},  // Invalid skills
                {"contact", {
                    {"phone", "123"},  // Invalid phone
                    {"address", "Short"},  // Too short
                    {"website", "not-a-url"}  // Invalid URL
                }}
            };
            
            std::cout << "Invalid data validation:\n";
            ValidationResult result = schema_manager.validateAgainstSchema(invalid_data, "user_profile");
            
            if (result.is_valid) {
                std::cout << "✓ Data is valid" << std::endl;
            } else {
                std::cout << "✗ Validation errors:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        // Save schema file
        {
            std::ofstream schema_file("user_profile_schema.json");
            schema_file << user_schema.dump(4);
            schema_file.close();
            std::cout << "\nSchema file saved to user_profile_schema.json" << std::endl;
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSON error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    
    return 0;
}