Skip to content

# Field Validation

Glaze provides field validation capabilities through compile-time options and customization points.

## Required Fields with `error_on_missing_keys`

By default, Glaze allows JSON objects to have missing fields when parsing. However, you can enable strict validation using the `error_on_missing_keys` option:

    struct config {
       std::string host;
       int port;
       std::optional<std::string> description;
    };

    std::string json = R"({"host":"localhost"})";  // Missing 'port'
    config obj;

    // Without error_on_missing_keys (default)
    auto ec = glz::read_json(obj, json);  // Success, port gets default value

    // With error_on_missing_keys
    ec = glz::read<glz::opts{.error_on_missing_keys = true}>(obj, json);  // Error: missing_key

### Default Behavior

When `error_on_missing_keys = true`, field requirements are determined automatically:

- Non-nullable types (e.g., `int`, `std::string`, `double`) are required
- Nullable types (e.g., `std::optional<T>`, `std::unique_ptr<T>`, `std::shared_ptr<T>`, `T*`) are optional

    struct user {
       std::string username;           // Required
       int id;                          // Required
       std::optional<std::string> bio;  // Optional
    };

    // Valid: optional field can be missing
    std::string json = R"({"username":"alice","id":42})";
    user u;
    auto ec = glz::read<glz::opts{.error_on_missing_keys = true}>(u, json);  // Success

    // Invalid: required field missing
    json = R"({"username":"alice"})";  // Missing 'id'
    ec = glz::read<glz::opts{.error_on_missing_keys = true}>(u, json);  // Error: missing_key

## Custom Field Requirements with `requires_key`

For fine-grained control over which fields are required, you can provide a `requires_key` function in your type's `glz::meta` specialization. This allows you to:

- Make non-nullable fields optional without wrapping them in `std::optional`
- Exclude internal/reserved fields from validation
- Implement complex business logic for field requirements

### Basic Usage

    struct api_request {
       std::string endpoint;      // Required
       std::string api_key;       // Required
       int timeout;               // Want this to be optional
       std::string _internal_id;  // Internal field, should not be required
    };

    template <>
    struct glz::meta<api_request> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          // Make timeout optional even though it's not nullable
          if (key == "timeout") {
             return false;
          }

          // Don't require internal fields
          if (key.starts_with("_")) {
             return false;
          }

          // Default: non-nullable fields are required
          return !is_nullable;
       }
    };

    // Now parsing works with optional timeout
    std::string json = R"({"endpoint":"/users","api_key":"secret123"})";
    api_request req;
    auto ec = glz::read<glz::opts{.error_on_missing_keys = true}>(req, json);  // Success!
    // req.timeout will have its default value (0)

### Function Signature

    static constexpr bool requires_key(std::string_view key, bool is_nullable)

Parameters: - `key`: The name of the field being checked - `is_nullable`: Whether the field's type is nullable (e.g., `std::optional`, pointers)

Returns: - `true` if the field is required (parsing will fail if it's missing) - `false` if the field is optional (parsing will succeed even if it's missing)

### Advanced Examples

#### Conditional Requirements

    struct form_data {
       std::string name;
       std::string email;
       std::string phone;
       std::string address;
    };

    template <>
    struct glz::meta<form_data> {
       // Require only name and at least one contact method (email or phone)
       // This is a simplified example - actual implementation would need runtime validation
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          if (key == "name") return true;  // Always required
          // For this compile-time function, we can only mark fields as optional or required
          // Runtime validation would be needed for "at least one of" requirements
          return false;
       }
    };

#### Reserved/Internal Fields

    struct database_record {
       int id;
       std::string name;
       int reserved_field1;  // Reserved for future use
       int reserved_field2;  // Reserved for future use
    };

    template <>
    struct glz::meta<database_record> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          // Don't require fields starting with "reserved"
          if (key.starts_with("reserved")) {
             return false;
          }
          return !is_nullable;
       }
    };

#### Working with Nullable Types

The `requires_key` function works seamlessly with nullable types:

    struct mixed_requirements {
       int id;                          // Required (non-nullable, no special rule)
       std::optional<int> optional_id;  // Optional (nullable type)
       int flexible_field;              // Made optional via requires_key
    };

    template <>
    struct glz::meta<mixed_requirements> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          // Make flexible_field optional
          if (key == "flexible_field") {
             return false;
          }
          // Default behavior: nullable types are optional, others are required
          return !is_nullable;
       }
    };

    // Valid: both optional_id and flexible_field can be missing
    std::string json = R"({"id":42})";
    mixed_requirements obj;
    auto ec = glz::read<glz::opts{.error_on_missing_keys = true}>(obj, json);  // Success

## Use Cases

### 1. Backward Compatibility

When adding new fields to an existing API, make them optional without changing their types:

    struct api_response_v2 {
       int status;
       std::string message;
       int new_field_added_in_v2;  // New field, should be optional for v1 clients
    };

    template <>
    struct glz::meta<api_response_v2> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          if (key == "new_field_added_in_v2") {
             return false;
          }
          return !is_nullable;
       }
    };

### 2. Configuration Files

Allow optional configuration values without cluttering code with `std::optional`:

    struct app_config {
       std::string database_url;  // Required
       int max_connections;       // Optional, has good default
       int timeout_ms;            // Optional, has good default
    };

    template <>
    struct glz::meta<app_config> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          if (key == "database_url") return true;
          return false;  // Everything else is optional
       }
    };

### 3. Partial Updates

When receiving partial update payloads, only require the ID field:

    struct user_update {
       int user_id;              // Always required
       std::string name;         // Optional - only update if provided
       std::string email;        // Optional - only update if provided
       std::string password;     // Optional - only update if provided
    };

    template <>
    struct glz::meta<user_update> {
       static constexpr bool requires_key(std::string_view key, bool is_nullable) {
          return key == "user_id";  // Only ID is required
       }
    };

## Relationship with JSON Schema

The `requires_key` customization point affects both:

1. Parsing/Validation: Determines which fields are validated during `glz::read` with `error_on_missing_keys = true`
2. JSON Schema Generation: Determines which fields appear in the `"required"` array when using `glz::write_json_schema`

This ensures consistency between your validation logic and your schema documentation.

## Best Practices

1. Use `std::optional` when field truly represents optional data - Reserve `requires_key` for cases where you need non-nullable types to be optional
2. Document your `requires_key` logic - Future maintainers should understand why certain fields are optional
3. Keep validation simple - Complex interdependent requirements may be better suited for runtime validation after parsing
4. Consider backward compatibility - Making a previously-optional field required is a breaking change
5. Test both paths - Ensure your code handles both the presence and absence of optional fields

## See Also

- Options - Compile-time options including `error_on_missing_keys`
- JSON Schema - Generating JSON schemas from C++ types
- Nullable Types - Working with optional and pointer types