nlohmann/json (バリデーション)

バリデーションライブラリC++JSONスキーマ検証nlohmannモダンC++

ライブラリ

nlohmann/json (バリデーション)

概要

nlohmann/jsonは、Modern C++のJSONライブラリとして最も人気の高いライブラリです。標準ライブラリのような直感的なAPIを提供し、JSONの読み書きを簡単に行えます。バリデーション機能は、サードパーティライブラリ(json-schema-validatorやvalijson)と組み合わせることで、強力なJSONスキーマ検証を実現できます。C++11以降に対応し、ヘッダーオンリーライブラリとして簡単に導入でき、STLコンテナのような操作感でJSONデータを扱うことができます。

詳細

nlohmann/jsonライブラリは、JSONの解析・生成・操作を行うためのモダンなC++ライブラリです。STLコンテナのような直感的なAPIを提供し、JSONオブジェクトをC++の値として自然に扱えます。バリデーション機能として、基本的な型チェック、構造検証、カスタムバリデーションロジックの実装をサポートしています。外部ライブラリとの連携により、JSON Schema Draft 4/7に準拠したスキーマバリデーションも可能です。パフォーマンスと使いやすさのバランスに優れ、企業レベルのプロダクションでも広く採用されています。

主な特徴

  • 直感的API: STLコンテナのような自然な操作感
  • 型安全性: 厳密な型チェックとエラーハンドリング
  • スキーマ検証: 外部ライブラリとの組み合わせによる高度なバリデーション
  • 高性能: 最適化されたJSON解析・生成エンジン
  • ヘッダーオンリー: 簡単な導入と配布
  • 豊富な機能: カスタムシリアライザー、イテレータ、パッチ操作など

メリット・デメリット

メリット

  • C++標準ライブラリのような直感的で使いやすいAPI
  • 優れたパフォーマンスと効率的なメモリ使用
  • 豊富なドキュメントと活発なコミュニティサポート
  • 多くの企業での採用実績と信頼性
  • 柔軟なカスタマイズ機能と拡張性
  • 包括的なエラーハンドリングとデバッグ支援

デメリット

  • コンパイル時間の増加(大きなヘッダーファイル)
  • 内蔵のスキーマバリデーション機能は限定的
  • 複雑なバリデーションには外部ライブラリが必要
  • 巨大なJSONファイルの処理にはメモリ使用量が多い
  • 学習コストが存在(特に高度な機能)

参考ページ

書き方の例

インストールと基本セットアップ

# vcpkgを使用してインストール
vcpkg install nlohmann-json

# Conanを使用してインストール
conan install nlohmann_json/3.11.2@

# CMakeでのfind_package使用例
find_package(nlohmann_json REQUIRED)
target_link_libraries(your_target nlohmann_json::nlohmann_json)

# パッケージマネージャーを使用しない場合
wget https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp
# json.hppを適切なディレクトリに配置

基本的なJSON操作とバリデーション

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

using json = nlohmann::json;

// 基本的なバリデーション機能
class JSONValidator {
public:
    // JSON型の確認
    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();
    }
    
    // 必須フィールドの確認
    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;
    }
    
    // 値の範囲チェック
    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;
    }
    
    // 文字列長の確認
    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;
    }
    
    // 配列サイズの確認
    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 {
        // JSONの基本的な作成とバリデーション
        {
            json person = {
                {"name", "田中太郎"},
                {"age", 30},
                {"email", "[email protected]"},
                {"skills", {"C++", "Python", "JavaScript"}},
                {"active", true}
            };
            
            std::cout << "Person JSON:\n" << person.dump(4) << std::endl;
            
            // 基本的なバリデーション
            if (JSONValidator::hasRequiredFields(person, {"name", "age", "email"})) {
                std::cout << "✓ 必須フィールドが揃っています" << std::endl;
            }
            
            if (JSONValidator::isStringLengthValid(person["name"], 2, 50)) {
                std::cout << "✓ 名前の長さが有効です" << std::endl;
            }
            
            if (JSONValidator::isInRange(person["age"], 0, 120)) {
                std::cout << "✓ 年齢が有効な範囲です" << std::endl;
            }
            
            if (JSONValidator::isArraySizeValid(person["skills"], 1, 10)) {
                std::cout << "✓ スキル配列のサイズが有効です" << std::endl;
            }
        }
        
        // JSONファイルの読み込みとバリデーション
        {
            // JSONファイルの読み込み
            std::ifstream config_file("config.json");
            if (config_file.is_open()) {
                json config;
                config_file >> config;
                
                // 設定ファイルのバリデーション
                std::vector<std::string> required_config = {"app_name", "port", "debug_mode"};
                if (JSONValidator::hasRequiredFields(config, required_config)) {
                    std::cout << "✓ 設定ファイルが有効です" << std::endl;
                } else {
                    std::cout << "✗ 設定ファイルに必須項目がありません" << std::endl;
                }
                
                config_file.close();
            }
        }
        
        // エラーハンドリングの例
        {
            json invalid_json = {
                {"name", 123},  // 文字列を期待するが数値
                {"age", "thirty"},  // 数値を期待するが文字列
                {"email", ""}  // 空の文字列
            };
            
            // 型チェック
            if (!JSONValidator::isString(invalid_json["name"])) {
                std::cout << "✗ 名前は文字列である必要があります" << std::endl;
            }
            
            if (!JSONValidator::isNumber(invalid_json["age"])) {
                std::cout << "✗ 年齢は数値である必要があります" << std::endl;
            }
            
            if (!JSONValidator::isStringLengthValid(invalid_json["email"], 5, 100)) {
                std::cout << "✗ メールアドレスの長さが無効です" << std::endl;
            }
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSONエラー: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
    }
    
    return 0;
}

高度なバリデーション機能とカスタム検証

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

using json = nlohmann::json;

// バリデーション結果
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());
        }
    }
};

// 高度なバリデーター
class AdvancedJSONValidator {
private:
    // メールアドレスの検証
    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の検証
    static bool isValidURL(const std::string& url) {
        static const std::regex url_regex(
            R"(^https?://[^\s/$.?#].[^\s]*$)"
        );
        return std::regex_match(url, url_regex);
    }
    
    // 日付の検証 (ISO 8601形式)
    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:
    // 文字列フィールドの高度な検証
    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;
        
        // フィールドの存在確認
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("必須フィールド '" + field_name + "' が見つかりません");
            }
            return result;
        }
        
        // 型チェック
        if (!j[field_name].is_string()) {
            result.addError("フィールド '" + field_name + "' は文字列である必要があります");
            return result;
        }
        
        std::string value = j[field_name].get<std::string>();
        
        // 長さチェック
        if (value.length() < min_length || value.length() > max_length) {
            result.addError("フィールド '" + field_name + "' の長さは " + 
                          std::to_string(min_length) + " から " + 
                          std::to_string(max_length) + " 文字である必要があります");
        }
        
        // パターンチェック
        if (!pattern.empty()) {
            std::regex regex_pattern(pattern);
            if (!std::regex_match(value, regex_pattern)) {
                result.addError("フィールド '" + field_name + "' がパターンに一致しません");
            }
        }
        
        // フォーマットチェック
        if (!format.empty()) {
            if (format == "email" && !isValidEmail(value)) {
                result.addError("フィールド '" + field_name + "' は有効なメールアドレスではありません");
            } else if (format == "url" && !isValidURL(value)) {
                result.addError("フィールド '" + field_name + "' は有効なURLではありません");
            } else if (format == "date" && !isValidDate(value)) {
                result.addError("フィールド '" + field_name + "' は有効な日付形式ではありません");
            }
        }
        
        return result;
    }
    
    // 数値フィールドの検証
    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;
        
        // フィールドの存在確認
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("必須フィールド '" + field_name + "' が見つかりません");
            }
            return result;
        }
        
        // 型チェック
        if (!j[field_name].is_number()) {
            result.addError("フィールド '" + field_name + "' は数値である必要があります");
            return result;
        }
        
        double value = j[field_name].get<double>();
        
        // 範囲チェック
        if (value < min_value || value > max_value) {
            result.addError("フィールド '" + field_name + "' の値は " + 
                          std::to_string(min_value) + " から " + 
                          std::to_string(max_value) + " の範囲である必要があります");
        }
        
        // 整数チェック
        if (integer_only && std::floor(value) != value) {
            result.addError("フィールド '" + field_name + "' は整数である必要があります");
        }
        
        return result;
    }
    
    // 配列フィールドの検証
    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;
        
        // フィールドの存在確認
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("必須フィールド '" + field_name + "' が見つかりません");
            }
            return result;
        }
        
        // 型チェック
        if (!j[field_name].is_array()) {
            result.addError("フィールド '" + field_name + "' は配列である必要があります");
            return result;
        }
        
        const json& array = j[field_name];
        
        // サイズチェック
        if (array.size() < min_items || array.size() > max_items) {
            result.addError("フィールド '" + field_name + "' のアイテム数は " + 
                          std::to_string(min_items) + " から " + 
                          std::to_string(max_items) + " である必要があります");
        }
        
        // 各アイテムの検証
        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("配列 '" + field_name + "' の要素[" + 
                                      std::to_string(i) + "]: " + error);
                    }
                }
            }
        }
        
        return result;
    }
    
    // オブジェクトフィールドの検証
    static ValidationResult validateObjectField(const json& j, const std::string& field_name,
                                              bool required = true,
                                              std::function<ValidationResult(const json&)> object_validator = nullptr) {
        ValidationResult result;
        
        // フィールドの存在確認
        if (!j.contains(field_name)) {
            if (required) {
                result.addError("必須フィールド '" + field_name + "' が見つかりません");
            }
            return result;
        }
        
        // 型チェック
        if (!j[field_name].is_object()) {
            result.addError("フィールド '" + field_name + "' はオブジェクトである必要があります");
            return result;
        }
        
        // オブジェクトの検証
        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("オブジェクト '" + field_name + "': " + error);
                }
            }
        }
        
        return result;
    }
};

// 具体的なスキーマバリデーション例
class UserProfileValidator {
public:
    static ValidationResult validateUserProfile(const json& user_profile) {
        ValidationResult result;
        
        // 基本情報の検証
        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);
        
        // スキルの検証
        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("スキルは文字列である必要があります");
                } else {
                    std::string skill = item.get<std::string>();
                    if (skill.empty() || skill.length() > 30) {
                        item_result.addError("スキルは1文字以上30文字以下である必要があります");
                    }
                }
                return item_result;
            }
        );
        result.merge(skills_result);
        
        // 連絡先情報の検証
        auto contact_result = AdvancedJSONValidator::validateObjectField(
            user_profile, "contact", false,
            [](const json& contact) -> ValidationResult {
                ValidationResult contact_result;
                
                // 電話番号の検証
                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);
                }
                
                // 住所の検証
                if (contact.contains("address")) {
                    auto address_result = AdvancedJSONValidator::validateStringField(
                        contact, "address", false, 10, 200
                    );
                    contact_result.merge(address_result);
                }
                
                // ウェブサイトの検証
                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 {
        // 有効なユーザープロファイルのテスト
        {
            json valid_profile = {
                {"name", "田中太郎"},
                {"email", "[email protected]"},
                {"age", 30},
                {"skills", {"C++", "Python", "JavaScript", "SQL"}},
                {"contact", {
                    {"phone", "+819012345678"},
                    {"address", "東京都新宿区新宿1-1-1"},
                    {"website", "https://example.com"}
                }}
            };
            
            std::cout << "有効なユーザープロファイル:\n" << valid_profile.dump(4) << std::endl;
            
            ValidationResult result = UserProfileValidator::validateUserProfile(valid_profile);
            
            if (result.is_valid) {
                std::cout << "✓ ユーザープロファイルは有効です" << std::endl;
            } else {
                std::cout << "✗ バリデーションエラー:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        std::cout << "\n" << std::string(50, '-') << "\n\n";
        
        // 無効なユーザープロファイルのテスト
        {
            json invalid_profile = {
                {"name", "A"},  // 短すぎる名前
                {"email", "invalid-email"},  // 無効なメール
                {"age", 150},  // 範囲外の年齢
                {"skills", {"", "very long skill name that exceeds the maximum length limit"}},  // 無効なスキル
                {"contact", {
                    {"phone", "123"},  // 無効な電話番号
                    {"address", "短い住所"},  // 短すぎる住所
                    {"website", "not-a-url"}  // 無効なURL
                }}
            };
            
            std::cout << "無効なユーザープロファイル:\n" << invalid_profile.dump(4) << std::endl;
            
            ValidationResult result = UserProfileValidator::validateUserProfile(invalid_profile);
            
            if (result.is_valid) {
                std::cout << "✓ ユーザープロファイルは有効です" << std::endl;
            } else {
                std::cout << "✗ バリデーションエラー:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSONエラー: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
    }
    
    return 0;
}

JSON Schema統合とスキーマ駆動バリデーション

// 注意: このコードはjson-schema-validatorライブラリとの統合例です
// 実際の使用には追加のライブラリが必要です

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

using json = nlohmann::json;

// スキーマ定義クラス
class JSONSchemaManager {
private:
    std::map<std::string, json> schemas;
    
public:
    // スキーマの定義と登録
    void defineSchema(const std::string& schema_name, const json& schema) {
        schemas[schema_name] = schema;
    }
    
    // ファイルからスキーマを読み込み
    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;
        }
    }
    
    // スキーマの取得
    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;
    }
    
    // 基本的なスキーマ検証(簡易版)
    ValidationResult validateAgainstSchema(const json& data, const std::string& schema_name) {
        ValidationResult result;
        
        auto schema_opt = getSchema(schema_name);
        if (!schema_opt) {
            result.addError("スキーマ '" + schema_name + "' が見つかりません");
            return result;
        }
        
        json schema = *schema_opt;
        
        // 型チェック
        if (schema.contains("type")) {
            std::string expected_type = schema["type"];
            if (!checkType(data, expected_type)) {
                result.addError("型が一致しません。期待される型: " + expected_type);
            }
        }
        
        // プロパティチェック
        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);
                }
            }
        }
        
        // 必須プロパティチェック
        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("必須プロパティ '" + prop_name + "' が見つかりません");
                }
            }
        }
        
        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;
        
        // 型チェック
        if (prop_schema.contains("type")) {
            std::string expected_type = prop_schema["type"];
            if (!checkType(value, expected_type)) {
                result.addError("プロパティ '" + prop_name + "' の型が一致しません");
            }
        }
        
        // 文字列の長さチェック
        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("プロパティ '" + prop_name + "' の長さが最小値を下回っています");
            }
        }
        
        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("プロパティ '" + prop_name + "' の長さが最大値を上回っています");
            }
        }
        
        // 数値の範囲チェック
        if (prop_schema.contains("minimum") && value.is_number()) {
            double minimum = prop_schema["minimum"];
            if (value.get<double>() < minimum) {
                result.addError("プロパティ '" + prop_name + "' の値が最小値を下回っています");
            }
        }
        
        if (prop_schema.contains("maximum") && value.is_number()) {
            double maximum = prop_schema["maximum"];
            if (value.get<double>() > maximum) {
                result.addError("プロパティ '" + prop_name + "' の値が最大値を上回っています");
            }
        }
        
        // 配列の長さチェック
        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("プロパティ '" + prop_name + "' の配列長が最小値を下回っています");
            }
        }
        
        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("プロパティ '" + prop_name + "' の配列長が最大値を上回っています");
            }
        }
        
        return result;
    }
};

// 使用例
int main() {
    try {
        JSONSchemaManager schema_manager;
        
        // ユーザープロファイルのスキーマ定義
        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);
        
        // 有効なデータの検証
        {
            json valid_data = {
                {"name", "田中太郎"},
                {"email", "[email protected]"},
                {"age", 30},
                {"skills", {"C++", "Python", "JavaScript"}},
                {"contact", {
                    {"phone", "+819012345678"},
                    {"address", "東京都新宿区新宿1-1-1"},
                    {"website", "https://example.com"}
                }}
            };
            
            std::cout << "有効なデータの検証:\n";
            ValidationResult result = schema_manager.validateAgainstSchema(valid_data, "user_profile");
            
            if (result.is_valid) {
                std::cout << "✓ データは有効です" << std::endl;
            } else {
                std::cout << "✗ バリデーションエラー:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        std::cout << "\n" << std::string(50, '-') << "\n\n";
        
        // 無効なデータの検証
        {
            json invalid_data = {
                {"name", "A"},  // 短すぎる
                {"email", "invalid-email"},  // 無効なメール
                {"age", 150},  // 範囲外
                {"skills", {"", "very long skill name that exceeds maximum length"}},  // 無効なスキル
                {"contact", {
                    {"phone", "123"},  // 無効な電話番号
                    {"address", "短い"},  // 短すぎる
                    {"website", "not-a-url"}  // 無効なURL
                }}
            };
            
            std::cout << "無効なデータの検証:\n";
            ValidationResult result = schema_manager.validateAgainstSchema(invalid_data, "user_profile");
            
            if (result.is_valid) {
                std::cout << "✓ データは有効です" << std::endl;
            } else {
                std::cout << "✗ バリデーションエラー:" << std::endl;
                for (const auto& error : result.errors) {
                    std::cout << "  - " << error << std::endl;
                }
            }
        }
        
        // スキーマファイルの保存
        {
            std::ofstream schema_file("user_profile_schema.json");
            schema_file << user_schema.dump(4);
            schema_file.close();
            std::cout << "\nスキーマファイルを user_profile_schema.json に保存しました" << std::endl;
        }
        
    } catch (const json::exception& e) {
        std::cerr << "JSONエラー: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "エラー: " << e.what() << std::endl;
    }
    
    return 0;
}