Skip to content

Annotations

This page lists the core annotations that can be used when defining schemas with Anvil.

Schema annotations

@Validate

Marks a class as a validated schema.

import io.github.anvil.Schema;
import io.github.anvil.annotations.Validate;

@Validate
public class User implements Schema {
    // fields...
}

Warning

The class must implement Schema. If it does not, your project will not compile.

Options:

  • value - Enable or disable validation for this class (default: true).
  • printInfo - When true, logs validation metadata for the class (default: false).
  • failFast - When true, stops validation on the first error (default: false).
@Validate(
    value = true,
    printInfo = false,
    failFast = false
)
public class User implements Schema {
    // fields...
}

Field annotations

@OptionalValue

Marks a field as optional during validation.

By default, all fields in a schema are required: if the input is missing a value for a field, Anvil produces a validation error. Annotating a field with @OptionalValue allows the field to be omitted without error.

public class User implements Schema {
    private String email; // required by default

    @OptionalValue
    private String nickname; // optional
}

@Inner

Marks a field as containing a nested schema that should be validated independently.

Options:

  • value - The schema class to use for validating the nested object. Must be a class annotated with @Validate that implements Schema.

When a field is annotated with @Inner, the field's value is extracted from the input as a nested object and validated using the schema class specified by the annotation's value. The nested validation follows the same rules as top-level schema validation, including all field annotations and validation rules defined in the nested schema class.

@Validate
public class Address implements Schema {
    private String street;
    private String city;
}

@Validate
public class User implements Schema {
    private String name;

    @Inner(Address.class)
    private Address address;
}

Recursive processing

Anvil processes nested schemas recursively. When validating a field annotated with @Inner:

  1. The nested input object is extracted from the parent input.
  2. The nested schema is validated independently using the same validation pipeline.
  3. All validation rules (annotations, postBuild() hooks) are applied to the nested schema.
  4. If validation succeeds, the validated nested schema instance is assigned to the field.
  5. If validation fails, errors are collected and prefixed with the field path.

Error path building

When nested validation fails, error messages are automatically prefixed with the full field path from the root element. This makes it clear which nested field has the error.

For example, with the following structure:

@Validate
public class Country implements Schema {
    @StrEqual("USA")
    private String code;
}

@Validate
public class Address implements Schema {
    @Inner(Country.class)
    private Country country;
}

@Validate
public class User implements Schema {
    @Inner(Address.class)
    private Address address;
}

If the code field in Country fails validation, the error message will show the full path:

for field 'address.country.code': Found value 'CAN', but expected equal to: 'USA'.

This path building works recursively for any depth of nesting, ensuring that errors always show the complete path from the root element to the field with the error.

Fail-fast behavior

The failFast setting on @Validate applies independently to each schema level. If a nested schema has failFast = true, validation stops on the first error within that nested schema, but the parent schema's failFast setting determines whether other fields at the parent level continue to be validated.

@List

Marks a field as containing a list/collection of nested schemas that should be validated independently.

Options:

  • value - The schema class to use for validating each element in the list. Must be a class annotated with @Validate that implements Schema.

When a field is annotated with @List, the field's value is extracted from the input as a list/collection and each element is validated using the schema class specified by the annotation's value. The validation follows the same rules as top-level schema validation, including all field annotations and validation rules defined in the schema class.

@Validate
public class Tag implements Schema {
    private String name;
    private String color;
}

@Validate
public class Post implements Schema {
    private String title;

    @List(Tag.class)
    private List<Tag> tags;
}

List element validation

Anvil processes list elements recursively. When validating a field annotated with @List:

  1. The list/collection is extracted from the input.
  2. Each element in the list is validated independently using the same validation pipeline.
  3. All validation rules (annotations, postBuild() hooks) are applied to each element.
  4. If validation succeeds for all elements, the validated list is assigned to the field.
  5. If validation fails for any element, errors are collected and prefixed with the element index and field path.

Error path building

When list element validation fails, error messages are automatically prefixed with the element index and full field path. This makes it clear which element and field has the error.

For example, with the following structure:

@Validate
public class Tag implements Schema {
    @StrEqual("red")
    private String color;
}

@Validate
public class Post implements Schema {
    @List(Tag.class)
    private List<Tag> tags;
}

If the color field in the second tag fails validation, the error message will show the full path:

for field 'tags[1].color': Found value 'blue', but expected equal to: 'red'.

This path building works for any depth of nesting, ensuring that errors always show the complete path from the root element to the field with the error, including the list index.

Fail-fast behavior

The failFast setting on @Validate applies independently to each schema level. If a schema used for list elements has failFast = true, validation stops on the first error within that element, but the parent schema's failFast setting determines whether other elements in the list continue to be validated.

String annotations

All string annotations live under io.github.anvil.annotations and are applied to String fields.

@Regex

Validates that a string matches a regular expression.

@Regex("^[a-zA-Z0-9_]{3,16}$")
private String username;

Options:

  • value - Regular expression pattern to match against the field value.

@StrEqual

Validates that a string exactly matches a given value (case-sensitive by default).

@StrEqual("ACTIVE")
private String status;

Options:

  • value - String value the field must equal.
  • strategy - Comparison strategy, using StringComparisonStrategy (CASE_SENSITIVE by default).

@StrIn

Validates that a string is one of a set of allowed values.

@StrIn({"admin", "user", "guest"})
private String role;

Options:

  • value - Array of allowed string values.
  • strategy - Comparison strategy, using StringComparisonStrategy (CASE_SENSITIVE by default).

@UUID

Validates that a string is a valid UUID format and transforms it into a UUID object.

import java.util.UUID;

@UUID
private UUID userId;

The validator checks that the string matches the standard UUID format with hyphens separating the five groups of hexadecimal digits (8-4-4-4-12). If valid, the string is converted to a java.util.UUID object and assigned to the field.

Enum annotations

@EnumValue

Validates that a field’s value is one of the constants of a given enum class.

public enum Role {
    ADMIN,
    USER,
    GUEST
}

public class User implements Schema {
    @EnumValue(Role.class)
    private Role role;
}

Options:

  • value - Enum class that defines the allowed constants.

Numeric annotations

All numeric annotations live in io.github.anvil.annotations.numeric and work with common numeric types (int, long, float, double and their wrappers). You can use either integer or floating-point literals; Anvil will cast them as needed.

@Equal

Validates that a numeric field equals a specific value.

@Equal(42.0)
private double answer;

Options:

  • value - Numeric value the field must equal.

@Between

Validates that a numeric field is between two values [min, max).

@Between(min = 0.0f, max = 100.0f)
private float percentage;

Options:

  • min - Inclusive lower bound for the field value.
  • max - Exclusive upper bound for the field value.

@Greater

Validates that a numeric field is strictly greater than a value.

@Greater(0.0f)
private float positiveNumber;

Options:

  • value - Threshold that the field value must be strictly greater than.

@GreaterOrEqual

Validates that a numeric field is greater than or equal to a value.

@GreaterOrEqual(18.0f)
private float age;

Options:

  • value - Threshold that the field value must be greater than or equal to.

@Less

Validates that a numeric field is strictly less than a value.

@Less(100.0f)
private float maxScore;

Options:

  • value - Upper bound that the field value must be strictly less than.

@LessOrEqual

Validates that a numeric field is less than or equal to a value.

@LessOrEqual(10.0f)
private float rating;

Options:

  • value - Upper bound that the field value must be less than or equal to.

@In

Validates that a numeric field is one of a discrete set of values.

@In({1.0, 2.0, 3.0})
private double level;

Options:

  • value - Array of allowed numeric values for the field.