title: Spring boot mvc validate enum by value
body: <p>In my spring boot application I want to validate enum by custom value:
  I have my DTO like following :</p>
  <pre class="lang-java prettyprint-override"><code>@Data
  public class PayOrderDTO {
      @NotNull
      @EnumValidator(enumClass = TransactionMethod.class)
      private TransactionMethod method;
  }
  </code></pre>
  <p>And my enum validator annotation defined like bellow:</p>
  <pre class="lang-java prettyprint-override"><code>@Retention(RetentionPolicy.RUNTIME)
  @Target({ElementType.FIELD, ElementType.METHOD})
  @Constraint(validatedBy = EnumValidatorImpl.class)
  public @interface EnumValidator {
      String message() default &quot;is invalid&quot;;
  
      /**
       * @return Specify group
       */
      Class&lt;?&gt;[] groups() default {};
  
      Class&lt;? extends Payload&gt;[] payload() default {};
  
      /**
       * @return Specifies the enumeration type. The parameter value must be a value in this enumeration type
       */
      Class&lt;? extends EnumBase&gt; enumClass();
  
      /**
       * @return Can it be null
       */
      boolean nullable() default false;
  
      /**
       * @return Values to exclude
       */
      int[] exclusion() default {};
  
  }
  </code></pre>
  <p>This is the implementation of my enum validator annotation</p>
  <pre class="lang-java prettyprint-override"><code>public class EnumValidatorImpl implements ConstraintValidator&lt;EnumValidator, EnumBase&gt; {
      private boolean nullable;
  
      private Set&lt;String&gt; values;
  
      @Override
      public void initialize(EnumValidator constraintAnnotation) {
          this.nullable = constraintAnnotation.nullable();
          Class&lt;? extends EnumBase&gt; enumClass = constraintAnnotation.enumClass();
          int[] exclusion = constraintAnnotation.exclusion();
  
          values = new HashSet&lt;&gt;();
          EnumBase[] enumConstants = enumClass.getEnumConstants();
          for (EnumBase iEnum : enumConstants) {
              values.add(iEnum.getValue());
          }
          if (exclusion.length &gt; 0)
              for (int i : exclusion) {
                  values.remove(i);
              }
      }
  
      @Override
      public boolean isValid(EnumBase param, ConstraintValidatorContext constraintValidatorContext) {
          if (nullable &amp;&amp; param == null) {
              return true;
          }
          else if(param == null)
              return false;
          return values.contains(param.getValue());
      }
  }
  
  </code></pre>
  <p>this is my enum:</p>
  <pre class="lang-java prettyprint-override"><code>public enum TransactionMethod implements EnumBase {
      CREDIT_CARD(&quot;creditcard&quot;),
      DEBIT_CARD(&quot;debitcard&quot;);
      public String label;
  
      TransactionMethod(String label) {
          this.label = label;
      }
  
      @Override
      public String getValue() {
          return this.label;
      }
  
      @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
      public static TransactionMethod fromString(String value) {
          return TransactionMethod.valueOf(value);
  //        return Arrays.stream(TransactionMethod.values())
  //                .filter(el -&gt; el.getValue().equals(value))
  //                .findFirst()
  //                .orElseThrow(() -&gt; {
  //                    throw new IllegalArgumentException(&quot;Not valid method&quot;);
  //                });
      }
  }
  </code></pre>
  <p>when I'm sending my http request to that rest controller :</p>
  <pre class="lang-java prettyprint-override"><code>@RequiredArgsConstructor
  @RestController
  @RequestMapping(&quot;/orders&quot;)
  @Validated
  public class PaymentRestController {
      
      public ResponseEntity&lt;?&gt; createPayment(
              @Valid @RequestBody PayOrderDTO payOrderDTO
      ) {
          return ResponseEntity.ok(&quot;Worked&quot;);
      }
  }
  
  </code></pre>
  <p>request example:</p>
  <pre><code>POST /orders/ HTTP/1.1
  Host: store.test
  Content-Type: application/json
  Content-Length: 152
  
  {
      &quot;method&quot;:&quot;creditcard&quot;,
  }
  </code></pre>
  <p>I'm expecting to get invalidation exception or error message defined in my enum validator, instead I get an exception in the console that contains :</p>
  <pre><code>JSON parse error: Cannot construct instance of `x.TransactionMethod`, problem: No enum constant x.TransactionMethod.creditcard
  </code></pre>
  <p>But if I sent this request :</p>
  <pre><code>POST /orders/ HTTP/1.1
  Host: store.test
  Content-Type: application/json
  Content-Length: 152
  
  {
      &quot;method&quot;:&quot;CREDIT_CARD&quot;,
  }
  </code></pre>
  <p>the application works normal</p>
  <p>I want to validate enum using label instead of constant value of the enum, if it doesn't exists, a validation error will be thrown like :</p>
  <pre><code>HTTP 422 : field 'method' is not valid, expected values are ['creditcard','debitcard']
  </code></pre>
  <p><strong>I tried some solutions as well like the convertor</strong></p>
  <pre class="lang-java prettyprint-override"><code>public class TransactionMethodStringEnumConverter implements Converter&lt;String, TransactionMethod&gt; {
      @Override
      public TransactionMethod convert(String source) {
          Optional&lt;TransactionMethod&gt; first = Arrays.stream(TransactionMethod.values()).filter(e -&gt; e.label.equals(source)).findFirst();
          return first.orElseThrow(() -&gt; {
              throw new IllegalArgumentException();
          });
      }
  
  }
  </code></pre>
  <p>but seems like I does nothing.
  I would really appreciate is someone has a good solution for this, Thank you </p>