so first of all i could not think of a better title for this question so i\'m open for changes.
I am trying to validate a bean using the bean validation mechanism (JSR-3
Try adding ?
like this:
data class User(
@field:Valid
@field:NotEmpty
var roles: MutableSet<@NotNull Role?> = HashSet()
)
Then the kotlin compiler should realise roles could be null
, and it might honor the validation, I know little about JSR380 so i'm just guessing though.
Make sure to compile the kotlin code with jvm target 1.8 or greater and enable this feature by providing the -Xemit-jvm-type-annotations
when compiling.
For Spring Boot projects you only have to do the following changes (tested with Spring Boot 2.3.3 and Kotlin 1.4.0):
<properties>
<java.version>11</java.version>
<kotlin.version>1.4.0</kotlin.version>
</properties>
<arg>-Xemit-jvm-type-annotations</arg>
to the kotlin-maven-plugin
:
<build>
<plugin>
<artifactId>kotlin-maven-plugin</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
<arg>-Xemit-jvm-type-annotations</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</build>
Sample Project
Rafal G. already pointed out that we could use a custom validator as a workaround. So here's some code:
The Annotation:
import javax.validation.Constraint
import javax.validation.Payload
import kotlin.annotation.AnnotationTarget.ANNOTATION_CLASS
import kotlin.annotation.AnnotationTarget.CONSTRUCTOR
import kotlin.annotation.AnnotationTarget.FIELD
import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.annotation.AnnotationTarget.TYPE_PARAMETER
import kotlin.annotation.AnnotationTarget.VALUE_PARAMETER
import kotlin.reflect.KClass
@MustBeDocumented
@Constraint(validatedBy = [NoNullElementsValidator::class])
@Target(allowedTargets = [FUNCTION, FIELD, ANNOTATION_CLASS, CONSTRUCTOR, VALUE_PARAMETER, TYPE_PARAMETER])
@Retention(AnnotationRetention.RUNTIME)
annotation class NoNullElements(
val message: String = "must not contain null elements",
val groups: Array<KClass<out Any>> = [],
val payload: Array<KClass<out Payload>> = []
)
The ConstraintValidator:
import javax.validation.ConstraintValidator
import javax.validation.ConstraintValidatorContext
class NoNullElementsValidator : ConstraintValidator<NoNullElements, Collection<Any>> {
override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext): Boolean {
// null values are valid
if (value == null) {
return true
}
return value.stream().noneMatch { it == null }
}
}
And finally the updated User class:
data class User(
@field:NotEmpty
@field:NoNullElements
var roles: MutableSet<Role> = HashSet()
)
Altough validation works now, the resulting ConstrainViolation is slightly different. For example the elementType
and propertyPath
differs as you can see below.
Java:
Kotlin:
Source is available here: https://github.com/DarkAtra/jsr380-kotlin-issue/tree/workaround
Thanks again for your help Rafal G.