Add annotations to a type without touch the original source?


#1

Hi does anyone knows if it is possible to use Kotlin extension functions to add custom annotations to one Class ?
I am looking for a easy way to use Jackson without having to annotate my domain model with Jackson annotations directly .

Thank you, kindly
Luis Oscar


#2

No, this is not possible. Kotlin extension functions do not modify the class they’re extending in any way; they are compiled to static methods in a separate class.


#3

With Jackson you can build your own custom serializers and deserializers per class. You thus could make your own builder/DSL for registering any type of class without the use of any annotations.


#4

Thank you, yole.
Sorry I did not formulate the question fully in the first iteration. I kind a new that this is not possible with extesion functions.
But I was wondering if this would be a nice to have language feature maybe.
For example Spring with CGlib generates we class byte code in runtime by wrapping the original type with a dynamically generated proxy, another example that is a little bit different is are the Groovy AST transformations using annotations but is this case we have to add this to the original source, and yet another example is Kotlin’s dynamic delegation using the “by” keyword that wrappers the original type.
So what I was wondering would it be a nice to have, a way to wrap the original type and have the compiler generate a wrapped type around the orignal one where we could for example add annotations not existing in the original one ?

Thank you, kindly
Luis Oscar


#5

Hi @mplatvoet,

Thank you, I am giving my first steps in Kotlin, :-). Do you know of one example that does something similar that I can look at ?

Cheers, Oscar


#6

The builder/DSL part may look something like this:

fun <T : Any> addType(builder: ClassConfig<T>.() -> Unit) {
    val config = JacksonClassConfig<T>()
    config.builder()
    //get serializer from builder and add it to jackson
}


interface ClassConfig<T : Any> {
    fun include(property: KProperty1<T, *>) = include(property, property.name)
    fun include(property: KProperty1<T, *>, fieldName: String)
}

class JacksonClassConfig<T : Any> : ClassConfig<T> {
    override fun include(property: KProperty1<T, *>, fieldName: String) {
        println("including: $fieldName for property: $property")
        //store the fields, build your own serializer    
    }
}

which you than would use like:

data class Person(val firstName: String, val lastName: String, val age: Int)

addType<Person> {
    include(Person::firstName)
    include(Person::lastName, "family-name")
}

How to build custom serialisers and deserialisers is described in the previous links.