Kotlin data class and bean validation with container element constraints
Asked Answered
S

2

24

With Bean Validation 2.0 it is possible to also put constraints on container elements.

I cannot get this to work with Kotlin data classes:

data class Some(val someMap: Map<String, @Length(max = 255) String>)

This does not have any effect. Any ideas?

I created a repository with a sample project to reproduce the case: https://github.com/mduesterhoeft/bean-validation-container-constraints

Stockpile answered 28/6, 2018 at 14:14 Comment(5)
Could you please add more details on how you are trying to make it work? It's not obvious from the question.Canine
@Canine I added a link to a sample project illustrating the issueStockpile
It looks like a problem with Kotlin-generated bytecode. I've added a version that uses a Java class for payload, and that version works fine github.com/empovit/bean-validation-container-constraints (have a look at the modified test). Apparently, Java retains the required information in runtime, while Kotlin does not.Canine
There is also a post in the kotlin forum - discuss.kotlinlang.org/t/…Stockpile
I created an entry in Kotlin bug tracker with the issue youtrack.jetbrains.net/issue/KT-26605Timely
U
3

Starting Kotlin 1.3.70 and 1.4, this should be possible setting a specific compiler option: https://kotlinlang.org/docs/reference/whatsnew14.html#type-annotations-in-the-jvm-bytecode .

On any previous version or any situation where this support is not sufficient, you have to write a custom validator.

Example one for validating that a collection only contains hex strings:

@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD,
    AnnotationTarget.ANNOTATION_CLASS,
    AnnotationTarget.CONSTRUCTOR,
    AnnotationTarget.VALUE_PARAMETER
)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Constraint(validatedBy = [HexStringElementsValidator::class])
annotation class HexStringElements(
    val message: String = "must only contain hex values",
    val groups: Array<KClass<*>> = [],
    val payload: Array<KClass<out Any>> = []
)

class HexStringElementsValidator : ConstraintValidator<HexStringElements, Collection<Any>> {

    companion object {
        val pattern = "^[a-fA-F0-9]+\$".toRegex()
    }

    override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext?) =
        value == null || value.all { it is String && pattern.matches(it) }
}
Unworthy answered 14/10, 2020 at 10:13 Comment(1)
Thanks for the hint. Adding the compiler argument Xemit-jvm-type-annotations worked for me - I created a branch in my sample project to show it is working github.com/mduesterhoeft/bean-validation-container-constraints/…Stockpile
P
8

Add this config to your build.gradle (note that ... means whatever is already there) :

Groovy:

compileKotlin {
    kotlinOptions {
        freeCompilerArgs = [..., "-Xemit-jvm-type-annotations"]
        ...
    }
}

Kotlin DSL:

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs = listOf(..., "-Xemit-jvm-type-annotations")
        ...
    }
}

Philipp answered 15/12, 2020 at 1:46 Comment(1)
Why would that improve anything? Can you elaborate on your answer?Cumberland
U
3

Starting Kotlin 1.3.70 and 1.4, this should be possible setting a specific compiler option: https://kotlinlang.org/docs/reference/whatsnew14.html#type-annotations-in-the-jvm-bytecode .

On any previous version or any situation where this support is not sufficient, you have to write a custom validator.

Example one for validating that a collection only contains hex strings:

@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD,
    AnnotationTarget.ANNOTATION_CLASS,
    AnnotationTarget.CONSTRUCTOR,
    AnnotationTarget.VALUE_PARAMETER
)
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
@Constraint(validatedBy = [HexStringElementsValidator::class])
annotation class HexStringElements(
    val message: String = "must only contain hex values",
    val groups: Array<KClass<*>> = [],
    val payload: Array<KClass<out Any>> = []
)

class HexStringElementsValidator : ConstraintValidator<HexStringElements, Collection<Any>> {

    companion object {
        val pattern = "^[a-fA-F0-9]+\$".toRegex()
    }

    override fun isValid(value: Collection<Any>?, context: ConstraintValidatorContext?) =
        value == null || value.all { it is String && pattern.matches(it) }
}
Unworthy answered 14/10, 2020 at 10:13 Comment(1)
Thanks for the hint. Adding the compiler argument Xemit-jvm-type-annotations worked for me - I created a branch in my sample project to show it is working github.com/mduesterhoeft/bean-validation-container-constraints/…Stockpile

© 2022 - 2024 — McMap. All rights reserved.