各種パターン
まず、動作確認用の適当なmainとcontrollerを作成する。
plugins { id 'org.springframework.boot' version '2.4.1' id 'io.spring.dependency-management' version '1.0.10.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation 'io.springfox:springfox-boot-starter:3.0.0' implementation 'org.springframework.boot:spring-boot-starter-validation' } test { useJUnitPlatform() }
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class SampleController { @PostMapping("/sample") public void sample(@RequestBody @Validated SampleRequest request) { System.out.println(request); } }
リクエスト格納クラスのプロパティの型を色々変えて試していく。
import lombok.Data; @Data public class SampleRequest { SimpleEnum simpleEnum; }
public enum SimpleEnum { on, off; }
単純なenum
上記のとおり、特に何もせずともenumにすればバインドする。上の例だと、"simpleEnum": "on"
とか"simpleEnum": "off"
が通る。また、"simpleEnum": null
とかjsonプロパティ自体が無い場合はnull
になる。
Optionalのenum
Optional<SimpleEnum> optSimpleEnum = Optional.empty();
この場合、"optSimpleEnum": null
を渡すとOptional.empty
になる。また、上記例はデフォルト値を入れており、jsonプロパティ自体が無い場合Optional.empty
になる。デフォルト値が無いとnull
になる。
独自マッピングのenum
たとえば、onは"1"
でoffは"0"
のように、enumとは異なる値からマッピングしたい場合。これは@JsonCreator
でマッピングを定義する。
import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.annotation.JsonCreator; public enum ValueEnum { on("1"), off("0"); String value; ValueEnum(String value) { this.value = value; } static Map<String, ValueEnum> BY_VALUE = new HashMap<>(); static { for (ValueEnum e : values()) { BY_VALUE.put(e.value, e); } } @JsonCreator public static ValueEnum create(String value) { return BY_VALUE.getOrDefault(value, ValueEnum.off); } }
上記例は、Map
にあらかじめマッピングを保持しておき、@JsonCreator
で文字列からenumへのマッピングを記述している。
なお、null
やjsonプロパティ自体が無い場合はnull
になる。
独自マッピングのOptionalのenum
Optional<ValueEnum> optValueEnum = Optional.empty();
この場合、null
を渡すとOptional.empty
になる。また、デフォルト値を入れているので、jsonプロパティ自体が無い場合もOptional.empty
になる。
独自マッピングのenumのvalidation
妥当な値以外はvalidationエラーにしたい場合。上記例の場合、"0"
, "1"
以外はvalidationエラーとしたい場合を考える。
@ValueEnumConstraint
ValidatedValueEnum validatedValueEnum;
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; import javax.validation.Constraint; import javax.validation.Payload; @Documented @Constraint(validatedBy = ValueEnumValidator.class) @Target( { ElementType.METHOD, ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface ValueEnumConstraint { String message() default "Invalid"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
import java.util.Objects; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class ValueEnumValidator implements ConstraintValidator<ValueEnumConstraint, ValidatedValueEnum> { @Override public boolean isValid(ValidatedValueEnum value, ConstraintValidatorContext context) { return Objects.nonNull(value); } }
public enum ValidatedValueEnum { //(ValueEnumと同一部分は省略) @JsonCreator public static ValidatedValueEnum create(String value) { return BY_VALUE.getOrDefault(value, null); } }
まず、@JsonCreator
で、妥当な値以外が来た場合はnull
を返す。そして、validationはnull
が来たらvalidationエラーにする。@JsonCreator
のバインディングの後にConstraintValidator
が走る。
ただし、これはnull
を許容したい場合にはvalidationエラーとなってしまう。
独自マッピングのOptionalのenumのvalidation
null
を許容するため、まずOptional
にする。
@OptValueEnumConstraint
Optional<OptValidatedValueEnum> optValidatedValueEnum = Optional.empty();
次に、enumにvalidationエラーを示すinvalid
を追加する。@JsonCreator
のマッピング時に、妥当な値以外はinvalid
を返す。
public enum OptValidatedValueEnum { on("1"), off("0"), invalid(""); //(省略) @JsonCreator public static OptValidatedValueEnum create(String value) { return BY_VALUE.getOrDefault(value, invalid); } }
そして、validationではinvalid
が来たらエラーにし、それ以外はOKにする。
public class OptValueEnumValidator implements ConstraintValidator<OptValueEnumConstraint, Optional<OptValidatedValueEnum>> { @Override public boolean isValid(Optional<OptValidatedValueEnum> value, ConstraintValidatorContext context) { if (value.isPresent()) { return !(value.get() == invalid); } return true; } }
これで、妥当な値、ここでは"0"
, "1"
, null
およびjsonプロパティ自体が無い、以外はvalidationエラーになる。