[JAVA] How to create a validator that allows only input to any one field

Introduction

How to extend Bean Validation: thermometer_face:

Confirmation environment

How to make a validator in the first place?

First, make an annotation

Mostly a copy of the code in Constraint composition: sweat:

AllowSingleInput.java


import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = {AllowSingleInputValidator.class}) //Class made with ↓
@Target({TYPE, ANNOTATION_TYPE}) //Allow it to be attached only to classes or annotations
@Retention(RUNTIME)
public @interface AllowSingleInput {
  String message() default "{com.neriudon.example.validator.SingleInput.message}";

  Class<?>[] groups() default {};

  Class<? extends Payload>[] payload() default {};

  String[] fields(); //Make an array

  @Target({TYPE, ANNOTATION_TYPE})
  @Retention(RUNTIME)
  @Documented
  @interface List {
    AllowSingleInput[] value();
  }
}

The point is to target the annotation only to the class or annotation, and to have an array of String in the field. You can write the message solidly, but I have set Please input to only one text filed in the properties.

Make a validator

Now, let's create a class that does the actual input check. Well, the basics are a copy of Example: sweat:

AllowSingleInputValidator.java


import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.util.StringUtils;

public class AllowSingleInputValidator implements ConstraintValidator<AllowSingleInput, Object> {
  private String[] fields;

  private String message;

  @Override
  public void initialize(AllowSingleInput constraintAnnotation) {
    fields = constraintAnnotation.fields(); //Get field name
    message = constraintAnnotation.message();
  }

  @Override
  public boolean isValid(Object value, ConstraintValidatorContext context) {
    BeanWrapper beanWrapper = new BeanWrapperImpl(value);
    //Process to check if any one field is entered
    boolean isSingleInput = false;
    for (String field : fields) {
      if (!StringUtils.isEmpty(beanWrapper.getPropertyValue(field))) {
        if (isSingleInput) {
          isSingleInput = false;
          break;
        } else {
          isSingleInput = true;
        }
      }
    }

    if (isSingleInput) {
      return true;
    } else {
      context.disableDefaultConstraintViolation();
      //Message output if conditions are not met
      context.buildConstraintViolationWithTemplate(message).addPropertyNode(fields[0])
          .addConstraintViolation();
      return false;
    }
  }
}

I get the target field name with ʻinitialize and check if it is entered in only one of the fields with ʻisValid (maybe there is another good way to write it: frowning2 :).

How to use

Annotate the Form class and set the field name to check the input in fields.

@AllowSingleInput(fields = {"todoTitle1", "todoTitle2", "todoTitle3"})
public class TodoForm implements Serializable {

  private static final long serialVersionUID = 1L;

  private String todoTitle1;

  private String todoTitle2;

  private String todoTitle3;

  // getter/omit setter
}

There is no need to change jsp.

<!--Extract only the Form-->
<form:form action="${pageContext.request.contextPath}/todo/create"
	method="post" modelAttribute="todoForm">
    <form:label path="todoTitle1"/>
    <form:input path="todoTitle1" />
    <form:errors path="todoTitle1" cssClass="text-error" /><br>
    <form:label path="todoTitle2"/>
    <form:input path="todoTitle2" />
    <form:errors path="todoTitle2" cssClass="text-error" /><br>
    <form:label path="todoTitle3"/>
    <form:input path="todoTitle3" />
    <form:errors path="todoTitle3" cssClass="text-error" /><br>
    <form:button>Create Todo</form:button>
</form:form>

Execution result

実行結果

You can now get an error message when you fill in multiple fields and press a button: v :.

Background to this post

As a memo because I needed this validator for the project I was involved in.

Recommended Posts

How to create a validator that allows only input to any one field
How to create a class that inherits class information
How to create a method
[Java] How to create a folder
[Rails 6] How to create a dynamic form input screen using cocoon
How to create a Maven repository for 2020
[Swift5] How to create a splash screen
[rails] How to create a partial template
How to quickly create a reverse proxy that supports HTTPS with Docker
We have released a service that allows you to easily create chats!
How to create a convenient method that utilizes generics and functional interfaces
How to create a database for H2 Database anywhere
[Rails] How to create a graph using lazy_high_charts
How to create pagination for a "kaminari" array
[Java] Create a collection with only one element
[1st] How to create a Spring-MVC framework project
How to easily create a pull-down in Rails
[Rails] How to create a Twitter share button
How to create a Java environment in just 3 seconds
[Rails] How to create a signed URL for CloudFront
How to create a JDBC URL (Oracle Database, Thin)
How to create a Spring Boot project in IntelliJ
[Spring Boot] How to create a project (for beginners)
How to create a data URI (base64) in Java
How to check only one RadioButton check by default (Note)
[Apple Subscription Offer] How to create a promotional offer signature
How to create docker-compose
How to create a lightweight container image for Java apps
How to create a form to select a date from the calendar
How to create a placeholder part to use in the IN clause
Create a method that can retrieve characters from any location
How to create a service builder portlet in Liferay 7 / DXP
[ruby] Creating a program that responds only to specific conditions
How to create and launch a Dockerfile for Payara Micro
[Swift5] How to create a .gitignore file and the code that should be written by default
How to create an application
How to leave a comment
How to insert a video
Create a name input function
How to create a jar file or war file using the jar command
How to create a registration / update function where the table crosses
How to implement a job that uses Java API in JobScheduler
How to create a new Gradle + Java + Jar project in Intellij 2016.03
How to deploy an app that references a local jar to heroku
How to identify the path that is easy to make a mistake
How to create a web server on an EC2 instance on AWS