Home > Software engineering >  Get annotation value via reflection
Get annotation value via reflection

Time:10-27

I am trying to get all the "Keys" of annotations for use later, I am initializing the value like this:

private val wgKeys = SpecialRequestContext::class.members.associate { m ->
    m.name to run {
        val headerAnnotation = m.annotations.find { a -> a is Header } as? Header
        headerAnnotation?.key
    }
}

Unfortunately, the result is a Map with name for keys (correct), but all the values are null. While debugging I see that m.annotations has no values.

Are annotations not available at this step?

Update: The minimum code to demonstrate this is here, unfortunately Kotlin playground cannot do reflection though:

@Target(AnnotationTarget.VALUE_PARAMETER)
annotation class Header(val key: String)

data class SpecialRequestContext(
    @Header("BK-Correlation-Id") val correlationId: String? = null,
    @Header("BK-Origin") val origin: String? = null,
    @Header("BK-Origin-City") val originCity: String? = null,
)

fun main() {
    println(wgKeys.count())
    println(wgKeys["origin"])
}

private val wgKeys = SpecialRequestContext::class.members.associate { m ->
        m.name to run {
            val headerAnnotation = m.annotations.find { a -> a is Header } as? Header
            headerAnnotation?.key
        }
    }

CodePudding user response:

I'm sure there's a better way of doing this, but basically you need to:

  1. get all the annotation's properties
  2. for each property, you need to reflectively get the value for that annotation instance (that's because 2 different classes could have the same annotation with different values).

A (rough) example:

annotation class Annotation1(val key1: String, val key2: Int)
annotation class Annotation2(val key3: String, val key4: Int)

@Annotation1(key1 = "value1", key2 = 2)
@Annotation2(key3 = "value3", key4 = 4)
class MyClass

fun main() {
    val myClassAnnotations = MyClass::class.annotations

    val propertiesByAnnotation =
        myClassAnnotations.associateWith { (it.annotationClass as KClass<Annotation>).declaredMemberProperties }

    val keyValuePerAnnotation = propertiesByAnnotation.map { (annotation, properties) ->
        annotation.annotationClass.simpleName!! to properties.map { property ->
            property.name to property.get(annotation)
        }

    }

    println(keyValuePerAnnotation)
}

This prints a list of pairs, where the first item is the annotation name and the second item is a list of key-value pairs for each of the annotation's properties. The output for that example is:

[(Annotation1, [(key1, value1), (key2, 2)]), (Annotation2, [(key3, value3), (key4, 4)])]

CodePudding user response:

Notice:

@Target(AnnotationTarget.VALUE_PARAMETER)

This means that the annotation is applied to the constructor parameters, not the properties. You won't find them on the properties.

To find them on the properties, you can change it to:

@Target(AnnotationTarget.PROPERTY)

If you need them applied to the parameters for some reason, you can find them like this:

private val wgKeys = SpecialRequestContext::class.primaryConstructor!!.parameters.associate { p ->
    p.name to p.annotations.filterIsInstance<Header>().firstOrNull()?.key
}
  • Related