Java
javaTutorial
Solve the problem that the @NotEmpty annotation fails when the List collection contains null elements
Solve the problem that the @NotEmpty annotation fails when the List collection contains null elements

When a List collection contains null elements, the standard @NotEmpty annotation cannot recognize it as an invalid state because it only checks whether the collection itself is null or empty. To solve this problem, you need to create custom validation annotations and corresponding validators to implement null value checking on the internal elements of the List collection, thereby ensuring data integrity and returning the expected 400 Bad Request response.
Understand the validation behavior of @NotEmpty and List collections
In Java Bean Validation, the @NotEmpty annotation is mainly used to verify whether a collection, array or string is empty. For collection types, it checks if the collection is null and contains any elements. However, when a List collection contains null elements, the @NotEmpty annotation does not treat it as "empty" or invalid.
Consider the following data model and request body example:
class Data {
@NotEmpty // This annotation only checks whether the List itself is empty, and does not check its internal elements private List values;
// Getter, Setter, Constructors
}
-
Request body 1 (expected 200 OK):
{ "values": ["randomValue"] }This request body is as expected, the List is not empty and contains valid elements.
-
Request body 2 (expected 400 BAD_REQUEST):
{ }The values field in this request body is missing. @NotEmpty will capture this situation and return 400.
-
Request body 3 (expected 400 BAD_REQUEST):
{ "values": [] }The values field in this request body is an empty List. @NotEmpty will capture this situation and return 400.
-
Request body 4 (actual 200 OK, expected 400 BAD_REQUEST):
{ "values": [null] }This is the core of the problem. Although the List contains null elements, since the List itself is not empty (it contains one element), the @NotEmpty annotation does not recognize this as an invalid state, causing the request to succeed (200 OK) instead of the expected validation error (400 BAD_REQUEST).
To solve this problem, we need to implement a custom validation logic that can deeply check each element in the List collection.
Implement custom List element validation
Custom validation usually involves two core components: a custom annotation (Annotation) and a validator (ConstraintValidator).
Step 1: Define the custom validation annotation @ValidList
First, create a new annotation, such as @ValidList. This annotation will be used to mark List fields that require custom validation.
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Constraint(validatedBy = ListValidator.class) //Specify the actual validator class public @interface ValidList {
String message() default "The list field cannot be null or contain null elements"; // Default error message Class>[] groups() default {}; // Validation group Class extends Payload>[] payload() default {}; // Payload information}
- @Constraint(validatedBy = ListValidator.class): This is the key part, which associates the @ValidList annotation with the ListValidator class we are about to create, indicating that ListValidator will be responsible for handling the validation logic of this annotation.
- message(): Defines the default error message returned when validation fails.
- groups() and payload(): attributes of standard Bean Validation annotations, used for group verification and passing additional information.
Step 2: Implement custom validator ListValidator
Next, create the ListValidator class, which will implement the ConstraintValidator interface and contain the actual validation logic.
import java.util.List;
import java.util.Objects;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class ListValidator implements ConstraintValidator<validlist list>> {
@Override
public void initialize(ValidList constraintAnnotation) {
// You can get the properties of the annotation here, such as customized error messages, etc.}
@Override
public boolean isValid(List> list, ConstraintValidatorContext context) {
// If the List itself is null, empty, or contains any null elements, it is considered invalid // Note: The logic here is that as long as the List itself is null, empty List, or there is any null element in the List, it is considered invalid.
// You can adjust this logic according to actual business needs.
if (list == null || list.isEmpty()) {
return false; // List is null or empty, considered invalid}
// Check if there is a null element in the List return list.stream().noneMatch(Objects::isNull);
}
}</validlist>
- ConstraintValidator
>: The first generic parameter is the custom annotation type, and the second generic parameter is the field type to which the annotation will be applied (here List>, representing any type of List). - initialize() method: called when the validator is initialized, and can be used to obtain the properties defined in the annotations.
- isValid() method: This is where the core validation logic lies.
- First check whether the incoming list is null or isEmpty(). If so, return false directly.
- Next, use list.stream().noneMatch(Objects::isNull) to check if there are any null elements in the List. If there are any null elements, noneMatch will return false, causing isValid to return false, indicating that validation failed.
- The logic here can be adapted to specific needs. For example, if you allow a List to be empty but not contain null elements, you can remove the list.isEmpty() check.
Step 3: Apply custom annotations to the data model
Finally, in your data model, replace or supplement the original @NotEmpty annotation with @ValidList.
import javax.validation.Valid;
import java.util.List;
public class Data {
@ValidList // Use our custom validation annotation private List values;
public List<string> getValues() {
return values;
}
public void setValues(List<string> values) {
this.values = values;
}
public Data() {
}
public Data(List values) {
this.values = values;
}
}</string></string>
With the above modification, when request body 4 { "values": [null] } is sent, ListValidator's isValid method will detect that the List contains null elements, thus returning false, triggering validation failure, and returning the expected 400 BAD REQUEST response.
Notes and Summary
Annotations for combined use: If you hope that the List itself cannot be null, cannot be empty, and cannot contain null elements, you can use @NotNull (used to check whether the List itself is null), @NotEmpty (used to check whether the List is empty) and @ValidList (used to check whether the internal elements of the List are null). But usually, the isValid method of @ValidList already contains checks for null and empty, so @ValidList can be used directly.
Error message internationalization: You can use placeholders (such as {my.custom.validation.message}) in the message attribute, and then define the corresponding internationalized message in the ValidationMessages.properties file.
-
Spring Boot integration: In a Spring Boot application, make sure you use the @Valid or @Validated annotation on your controller method parameters to activate Bean Validation.
@RestController @RequestMapping("/api/data") public class DataController { @PostMapping public ResponseEntity<string> createData(@Valid @RequestBody Data data) { // Process business logic return ResponseEntity.ok("Data created successfully"); } }</string> Flexibility: The isValid method of a custom validator provides great flexibility. You can implement more complex validation logic according to business needs, such as checking the uniqueness of elements in a List, meeting specific format requirements, etc.
By customizing validation annotations and validators, we can effectively extend the functionality of Bean Validation and handle complex validation scenarios that cannot be met by standard annotations, thereby improving the data integrity and robustness of the application.
The above is the detailed content of Solve the problem that the @NotEmpty annotation fails when the List collection contains null elements. For more information, please follow other related articles on the PHP Chinese website!
Hot AI Tools
Undress AI Tool
Undress images for free
AI Clothes Remover
Online AI tool for removing clothes from photos.
Undresser.AI Undress
AI-powered app for creating realistic nude photos
ArtGPT
AI image generator for creative art from text prompts.
Stock Market GPT
AI powered investment research for smarter decisions
Hot Article
Popular tool
Notepad++7.3.1
Easy-to-use and free code editor
SublimeText3 Chinese version
Chinese version, very easy to use
Zend Studio 13.0.1
Powerful PHP integrated development environment
Dreamweaver CS6
Visual web development tools
SublimeText3 Mac version
God-level code editing software (SublimeText3)
Hot Topics
20518
7
13631
4
How to configure Spark distributed computing environment in Java_Java big data processing
Mar 09, 2026 pm 08:45 PM
Spark cannot run in local mode, ClassNotFoundException: org.apache.spark.sql.SparkSession. This is the most common first step of getting stuck: even the dependencies are not correct. Only spark-core_2.12 is written in Maven, but spark-sql_2.12 is not added. SparkSession crashes as soon as it is built. The Scala version must strictly match the official Spark compiled version - Spark3.4.x uses Scala2.12 by default. If you use spark-sqljar of 2.13, the class loader cannot directly find the main class. Practical advice: Go to mvnre
How to safely map user-entered weekday string to integer value and implement date offset operation in Java
Mar 09, 2026 pm 09:43 PM
This article introduces a concise and maintainable way to map the weekday string (such as "Monday") to the corresponding serial number (1-7), and use the modulo operation to realize the forward and backward offset of any number of days (such as Monday plus 4 days to get Friday), avoiding lengthy if chains and hard-coded logic.
How to generate a list of duplicate elements using Java's Collections.nCopies_Initialization tips
Mar 06, 2026 am 06:24 AM
Collections.nCopies returns an immutable view. Calling add/remove will throw UnsupportedOperationException; it needs to be wrapped with newArrayList() to modify it, and it is disabled for mutable objects.
How to use Homebrew to install Java on Mac_A must-have Java tool chain for developers
Mar 09, 2026 pm 09:48 PM
Homebrew installs the latest stable version of openjdk (such as JDK22) by default, not the LTS version; you need to explicitly execute brewinstallopenjdk@17 or brewinstallopenjdk@21 to install the LTS version, and manually configure PATH and JAVA_HOME to be correctly recognized by the system and IDE.
What is exception masking (Suppressed Exceptions) in Java_Multiple resource shutdown exception handling
Mar 10, 2026 pm 06:57 PM
What is SuppressedException: It is not "swallowed", but actively archived by the JVM. SuppressedException is not an exception loss, but the JVM quietly attaches the secondary exception to the main exception under the premise that "only one exception must be thrown" for you to verify afterwards. It is automatically triggered by the JVM in only two scenarios: one is that the resource closure in try-with-resources fails, and the other is that you manually call addSuppressed() in finally. The key difference is: the former is fully automatic and safe; the latter requires you to keep it to yourself, and it can be written as shadowing if you are not careful. try-
How to correctly implement runtime file writing in Java applications (avoiding JAR internal write failures)
Mar 09, 2026 pm 07:57 PM
After a Java application is packaged as a JAR, data cannot be written directly to the resources in the JAR package (such as test.txt) because the JAR is essentially a read-only ZIP archive; the correct approach is to write variable data to an external path (such as a user directory, a temporary directory, or a configuration-specified path).
What is the underlying principle of array expansion in Java_Java memory dynamic adjustment analysis
Mar 09, 2026 pm 09:45 PM
ArrayList.add() triggers expansion because grow() is called when size is equal to elementData.length. The first add allocates 10 capacity, and subsequent expansion is 1.5 times and not less than the minimum requirement, relying on delayed initialization and System.arraycopy optimization.
How to safely read a line of integer input in Java and avoid Scanner blocking
Mar 06, 2026 am 06:21 AM
This article introduces typical blocking problems when using Scanner to read multiple integers in a single line. It points out that hasNextInt() will wait indefinitely when there is no subsequent input, and recommends a safe alternative with nextLine() string splitting as the core.





