Java lightweight validator.
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 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(baseClass = String.class, firstGroup = true, maxSize = 3)
Object reference;
@Extension({"jpg", "png", "bmp"})
Path photograph;
// Constructors , getters, setters, etc.
}
class Address {
@Required
@Size(max = 50)
String streetNumberName;
@Size(max = 4)
String apartment;
@Required
String city;
// 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 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"));
/* 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 or String or Collection<String> (max 3 items).
photograph extension must be one of [jpg, png, bmp].
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.
- 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.
- If the value is null or an empty group:
- Examples:
@Required private Long id; @Required private List<Adress> addresses;
- Description: Checks that a value is within a closed range.
- Applies to: Fields of any numeric type.
- Parameters:
- min: The lowest value of the range. By default is Double.MIN_VALUE.
- max: The greatest value of the range. By default is Double.MAX_VALUE.
- 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}.
- If min was set and the value is lower:
- Example:
@Range(min = 0.0, max = 100.0) private Double grade;
- 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.
- 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.
- 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}.
- If min was set and the length or size is lower:
- Example:
@Size(min = 2, max = 5) private List<Project> projects;
- Description: Checks that the file extension is one of an expected list.
- Applies to: Fields of type: java.nio.file.Path, java.io.File.
- Parameters:
- value: Array of expected extensions. Mandatory.
- Error messages:
- If file extension is not any of the value array:
- extension must be one of {value}.
- If file extension is not any of the value array:
- Example:
@Extension({"doc", "xls", "txt"}) private Path evidenceFile;
- Description: Checks that the type of an object is one of a list of candidate types.
- Applies to: Fields of the Object type, including Collection of objects or Collection of Collection of objects. Collection can be any subinterface such as: List, Set, etc.
- Parameters:
- baseClass: A candidate base class for the field. Mandatory.
- firstGroup: A boolean to indicate if the type is a Collection of the base class. By default is false.
- secondGroup: A boolean to indicate if the type is a Collection of Collection of the base class. By default is false.
- maxSize: The greatest size of the first Collection if set. By default is 0.
- Error messages for each @ObjectType:
- If only baseClass was set and the field type does not match:
- {baseClass}
- If firstGroup was set and the field is not Collection of the baseClass:
- Collection<{baseClass}>
- If firstGroup and maxSize were set and the field is not a Collection of the baseClass or collection size is greater than maxSize:
- Collection<{baseClass}> (max {maxSize} items)
- If firstGroup and secondGroup were set and the field is not a Collection of Collection of the baseClass:
- Collection<Collection<{baseClass}>>
- If firstGroup, secondGroup and maxSize were set aand the field is not a Collection of Collection of the baseClass or first collection size is greater than maxSize:
- Collection<Collection<{baseClass}>> (max {maxSize} items)
- If only baseClass was set and the field type does not match:
- Error message for all @ObjectType:
- If the field type does not match any ObjectType:
- type must be or {msg for ObjectType 1} or {msg for ObjectType 2} ... or {msg for ObjectType N}.
- If the field type does not match any ObjectType:
- Example:
@ObjectType(baseClass = String.class) @ObjectType(baseClass = String.class, firstGroup = true, maxSize = 2) @ObjectType(baseClass = String.class, firstGroup = true, secondGroup = true, maxSize = 2) Object reference;
- 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 constraint (Required, Range, Size, ObjectType) enable nested validation automatically.
- Parameters:
- (none).
- Error messages:
- (none).
- Example:
@Valid private Address mainAddress;
For creating a new constraint you need to create both a new constraint annotation and a new validator class:
Create a new annotation YourNewConstraint
with the following template:
@Documented
@Constraint(validatedBy = YourNewValidator.class)
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface YourNewConstraint {
String message() default "<your custom message when validation fails>.";
// Add any other annotation methods needed by your new constraint.
}
- Use the
@Constraint
annotation to linkYourNewConstraint
annotation toYourNewValidator
class. - Define at least the
message()
method. This is the message to show when validation fails. Here you can use optionally:- Curly brackets to reference other annotation methods. For example:
some text with {max} value.
. The message includes the value of the annotation methodmax()
. - Conditional segments based on the value of some annotation method. For example:
#if(max)some text with {max} value.#endif
. The message includes the value of the annotation methodmax()
and it will be shown only if themax()
is not empty. In this context, "empty" depends on the the annotation method type:- If boolean, empty means the value is false.
- If String, empty means the text is empty.
- If double, empty means the number is Double.MIN_VALUE or Double.MAX_VALUE.
- If int, empty means the number is zero or Integer.MAX_VALUE.
- If Class, empty means the class is equals to javax.lang.model.type.NullType.
- If array, empty means the array has no elements.
- Loop segments for constraint annotations defined as arrays. For example:
type must be#for(value) or {message}#endfor.
. That message will concatenate themessage()
of each constraint in the constraint array. The argumentvalue
is not meaningful.
- Curly brackets to reference other annotation methods. For example:
- Add any other annotation methods needed by your new constraint.
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 boolean isValid(Object value) {
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 useObject
if your validations applies to more than one class. - Create as field members as annotation methods you have in YourNewConstraint, excluding message().
- Overrides the
initialize()
method to capture the annotation method values in your field members. - Overrides the
isValid()
method to do the validation logic. 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.
Please read our Contributing guide to learn and understand how to contribute to this project.
This library is licensed under the MIT License. See the LICENSE file for more information.