这是indexloc提供的服务,不要输入任何密码
Skip to content

sashirestela/slimvalidator

Repository files navigation

🛡 SlimValidator

Java lightweight validator.

Quality Gate Status codecov Maven Central GitHub Workflow Status Ask DeepWiki

Table of Contents

💡 Description

SlimValidator is a Java library for providing object validation through annotations. It is inspired by the Java Bean Validation specification but is not a implementation at all.

For example, to validate the object of a class, we need to annotate its class or fields with constraints and then use the Validator class to evaluate whether the object meets all the constraints:

/* Classes definition with constraint annotations */

class Person {

  @Required
  Integer id;

  @Required
  @Size(max = 50)
  String fullName;

  @Required
  Gender gender;
  
  @Range(min = 2000)
  Double income;

  @Size(min = 3, max = 5)
  String[] hobbies;

  @Valid
  Address address;

  @ObjectType(baseClass = {String.class})
  @ObjectType(schema = Schema.COLL, baseClass = {String.class}, maxSize = 3)
  Object reference;

  @Extension({"jpg", "png", "bmp"})
  Path photograph;

  @Valid
  User user;

  // Constructors , getters, setters, etc.

}

class Address {

  @Required
  @Size(max = 50)
  String streetNumberName;

  @Size(max = 4)
  String apartment;

  @Required
  String city;

  // Constructors , getters, setters, etc.

}

@RequiredIfNull(fields = {"userName"}, dependsOn = "email")
@FieldMatch(first = "password", second = "confirmPassword")
class User {
  
  String username;

  String email;

  @Required
  String password;

  @Required
  String confirmPassword;

  // Constructors , getters, setters, etc.

}

enum Gender {
  MALE,
  FEMALE,
  OTHER
}

/* Instantiate objects of those classes */

var address = new Address();
address.setStreetNumberName("1765 Paramount Avenue");
address.setApartment("123-A");

var user = new User();
user.setPassword("q1w2e3");
user.setConfirmPassword("q1w2e3");

var person = new Person();
person.setFullName("Martin Jefferson");
person.setGender(Gender.MALE);
person.setIncome(1850.5);
person.setHobbies(new String[] {"dancing", "running"});
person.setAddress(address);
person.setReference(List.of(10, 20));
person.setPhotograph(Paths.get("src/test/resources/sample.txt"));
person.setUser(user);

/* Validate objects */

var validator = new Validator();
var violations = validator.validate(person);
if (violations.size() > 0) {
  violations.forEach(v -> System.out.println(v.getName() + " " + v.getMessage()));
}

As a result of the validation process, you will see the following messages in console, because the object does not meet several constraints:

id must have a value.
income must be at least 2000.
hobbies size must be at least 3 at most 5.
address.apartment size must be at most 4.
address.city must have a value.
reference type must be one of String or Collection<String> and size at most 3.
photograph extension must be one of [jpg, png, bmp].
in user [username] must have a value when email is null.

⚙ Installation

You can install this library by adding the following dependency to your Maven project:

<dependency>
    <groupId>io.github.sashirestela</groupId>
    <artifactId>slimvalidator</artifactId>
    <version>[latest version]</version>
</dependency>

Or alternatively using Gradle:

dependencies {
    implementation 'io.github.sashirestela:slimvalidator:[latest version]'
}

NOTE: Requires Java 11 or greater.

🎯 Field-Level Constraints

@Required

  • Description: Checks that a value is not null. In case the value is a group (Collection, Map, Array) checks that it is not empty.
  • Applies to: Fields of any type.
  • Parameters:
    • (none).
  • Error messages:
    • If the value is null or an empty group:
      • must have a value.
  • Examples:
    @Required
    private Long id;
    
    @Required
    private List<Adress> addresses;

@Range

  • Description: Checks that a numeric value is within a closed range.
  • Applies to: Fields of any numeric type.
  • Exceptions:
    • When neither min nor max are set.
    • When min is not less than max.
    • When the value is of an unexpected type, unless isVariableType is true.
  • Parameters:
    • min: The lowest value of the range. By default is -Double.MAX_VALUE.
    • max: The greatest value of the range. By default is Double.MAX_VALUE.
    • isVariableType: Whether the field type is variable. The default value is false.
  • Error messages:
    • If min was set and the value is lower:
      • must be at least {min}.
    • If max was set and the value is greater:
      • must be at most {max}.
    • If min and max were set and the value is out of range:
      • must be at least {min} at most {max}.
  • Example:
    @Range(min = 0.0, max = 100.0)
    private Double grade;

@Size

  • Description: Checks that a text's length or a group's size is within a closed range.
  • Applies to: Fields of type: String, Collection, Map, Array.
  • Exceptions:
    • When min is greater than max.
    • When the value is of an unexpected type, unless isVariableType is true.
  • Parameters:
    • min: The lowest value of the length or size. By default is 0.
    • max: The greatest value of the length or size. By default is Integer.MAX_VALUE.
    • isVariableType: Whether the field type is variable. The default value is false.
  • Error messages:
    • If min was set and the length or size is lower:
      • size must be at least {min}.
    • If max was set and the length or size is greater:
      • size must be at most {max}.
    • If min and max were set and the length or size is out of range:
      • size must be at least {min} at most {max}.
  • Example:
    @Size(min = 2, max = 5)
    private List<Project> projects;

@Extension

  • Description: Checks that the file extension is one of an expected list.
  • Applies to: Fields of type: java.nio.file.Path or java.io.File.
  • Exceptions:
    • When the underlying filename is empty.
    • When the filename extension is bad formed.
    • When the value is of an unexpected type, unless isVariableType is true.
  • Parameters:
    • value: Array of expected extensions. Mandatory.
    • isVariableType: Whether the field type is variable. The default value is false.
  • Error messages:
    • If file extension is not any of the value array:
      • extension must be one of {value}.
  • Example:
    @Extension({"doc", "xls", "txt"})
    private Path evidenceFile;

@ObjectType

  • Description: Checks that the type of an object matches a specific schema pattern. Supports direct objects, collections, nested collections, maps, and maps with collection values.
  • Applies to: Fields of the Object type, including various collection and map structures.
  • Parameters:
    • schema: The schema pattern to validate against. Options: DIRECT, COLL, COLL_COLL, MAP, MAP_COLL. By default is DIRECT.
    • baseClass: Array of candidate base classes for the field. Mandatory.
    • keyClass: The class type for map keys when using MAP or MAP_COLL schema. By default is void.class.
    • maxSize: The maximum size of the outer collection or map. By default is Integer.MAX_VALUE.
    • maxInnerSize: The maximum size of inner collections. By default is Integer.MAX_VALUE.
    • maxChecks: The maximum number of items to check for performance optimization. By default is 20.
    • allowNull: Whether to allow null values in collections or maps. By default is true.
    • allowInnerNull: Whether to allow null values in inner collections. By default is true.
  • Schema Types:
    • DIRECT: Validates direct object types (baseClass)
    • COLL: Validates Collection<baseClass>
    • COLL_COLL: Validates Collection<Collection<baseClass>>
    • MAP: Validates Map<keyClass, baseClass>
    • MAP_COLL: Validates Map<keyClass, Collection<baseClass>>
  • Error messages:
    • Dynamically generated based on schema type and configuration parameters
  • Example:
    // directReference could be: String or Integer
    @ObjectType(baseClass = {String.class, Integer.class})
    private Object directReference;
    
    // mapReference could be: Map<String, Double> or Map<String, Integer>
    @ObjectType(schema = Schema.MAP, keyClass = String.class, baseClass = {Double.class, Integer.class})
    private Object mapReference;
    
    // multiSchemaReference could be: String or List<String> or List<List<String>>
    @ObjectType(baseClass = {String.class})
    @ObjectType(schema = Schema.COLL, baseClass = {String.class}, maxSize = 2)
    @ObjectType(schema = Schema.COLL_COLL, baseClass = {String.class}, maxSize = 2)
    private Object multiSchemaReference;

@Valid

  • Description: Flag to do nested validation for fields without any constraint.
  • Applies to: Fields of custom classes that do not have any constraint but it requires to validate their nested fields. Any field-level constraint enable nested validation automatically.
  • Parameters:
    • (none).
  • Error messages:
    • (none).
  • Example:
    @Valid
    private Address mainAddress;

📦 Class-Level Constraints

@RequiredIfNull

  • Description: Checks that all fields in a list are not null when the dependsOn field is null.
  • Applies to: Fields of any type.
  • Parameters:
    • fields: Array of field names to evaluate. Mandatory.
    • dependsOn: Name of the reference field. Mandatory.
  • Error messages:
    • If any of the fields is null when the dependsOn field is null:
      • {fields} must have a value when {dependsOn} is null.
  • Examples:
    @RequiredIfNull(fields = {"firstName", "lastName"}, dependsOn = "fullName")
    class User {
        private String firstName;
        private String lastName;
        private String fullName;
    }

@FieldMatch

  • Description: Checks that two fields match.
  • Applies to: Fields of any type.
  • Parameters:
    • first: First field name. Mandatory.
    • second: Second field name. Mandatory.
  • Error messages:
    • If both fields do not match:
      • {first} and {second} must match.
  • Examples:
    @FieldMatch(first = "password", second = "confirmPassword")
    class User {
        private String password;
        private String confirmPassword;
    }

🪝 Create New Constraint

For creating a new constraint you need to create both a new constraint annotation and a new validator class:

New Constraint Annotation

Create a new annotation YourNewConstraint with the following template:

@Documented
@Constraint(validatedBy = YourNewValidator.class)
@Target({ ElementType.FIELD })  // For filed-level constraint OR
@Target({ ElementType.TYPE })   // For class-level constraint
@Retention(RetentionPolicy.RUNTIME)
public @interface YourNewConstraint {

    // Add any annotation methods needed by your new constraint.

}
  • Use the @Constraint annotation to link YourNewConstraint annotation to YourNewValidator class.
  • Add any other annotation methods needed by your new constraint.

New Validator Class

Create a new class YourNewValidator with the following template:

public class YourNewValidator implements ConstraintValidator<YourNewConstraint, ClassOfObjectsToValidate> {

    private Type1 annotMethod1;
    private Type2 annotMethod2;
    ...

    @Override
    public void initialize(YourNewConstraint annotation) {
        annotMethod1 = annotation.annotMethod1();
        annotMethod2 = annotation.annotMethod2();
        ...
    }

    @Override
    public String getMessage() {
        // This the message to output when object violates the constraint.
        // Prepare it using the annotation methods values gathered in initialize().
        // Example:
        // return "Custom message with " + annotMethod1 + " and " + annotMethod2;
    }

    @Override
    public boolean isValid(Object value) {
        // This condition applies for field-level constraints only
        if (value == null) {
            return true;
        }

        // Add your validation logic here
        
        return validationResult;
    }

}
  • Implement the ConstraintValidator<A, T> interface, where A represents YourNewConstraint and T represents the class of the objects to validate, in this case, you can use Object if your validations applies to more than one class.
  • Create as field members as annotation methods you have in YourNewConstraint.
  • Override the initialize() method to capture the annotation method values in your field members.
  • Override the getMessage() method to build the error message using the annotation method values.
  • Override the isValid() method to do the validation logic. For field-level constraints only: your first validation step must return true if the object to validate is null, because we have the annotation @Required to validate that condition, we don't want to evaluate that nullity here.

💼 Contributing

Please read our Contributing guide to learn and understand how to contribute to this project.

📄 License

This library is licensed under the MIT License. See the LICENSE file for more information.

Packages

No packages published

Contributors 2

  •  
  •  

Languages