Rani
Rani

Reputation: 6792

How to get Jackson JsonProperty of enum values in Kotlin?

I'm building a schema generator and I'm trying to get the JsonProperty of enum values, for example, for this class:

enum class Size {
    @JsonProperty("really-tall") TALL,
    @JsonProperty("really-grande") GRANDE;
}

I'd like to get the list "really-tall", "really-grande".

How do I access the annotation of an enum? Thanks!

UPDATE:

Solution based on this reply for a generic KType:

return (jvmErasure.java as Class<Enum<*>>)
  .enumConstants
  .map {
    it.javaClass.getField(it.name).getAnnotation(JsonProperty::class.java)?.value // Get the JsonProperty string first if exists
      ?: it.name
  }

Upvotes: 1

Views: 1252

Answers (2)

dnault
dnault

Reputation: 8909

Here's a Kotlin implementation of the technique suggested by user @aSemy.

It's an extension function on ObjectMapper for asking the mapper how it would serialize the values of an enum. This is more robust that just inspecting the @JsonProperty annotation, since it works with @JsonValue as well as any custom annotation introspectors registered with the mapper.

inline fun <reified T : Enum<T>> ObjectMapper.enumValues() : List<String> {
    return convertValue(
        kotlin.enumValues<T>(),
        jacksonTypeRef<List<String>>()
    )
}

Usage:

println(jsonMapper().enumValues<Size>())

Upvotes: 0

Roar S.
Roar S.

Reputation: 11339

Update: Additional question from OP

How do I make the first approach work for a generic KType

inline fun <reified T : Enum<T>> getJsonPropertyAnnotations() = enumValues<T>().map {
    it.declaringClass
        .getField(it.name)
        .getAnnotation(JsonProperty::class.java)
        .value
}

class SomeTest : StringSpec({
    "getJsonPropertyAnnotations" {
        getJsonPropertyAnnotations<Size>() 
            shouldBe listOf("really-tall", "really-grande")
    }
})

Please note that with Kotlin 1.7, IntelliJ may show a deprecation warning with wrong replacement for declaringClass in getJsonPropertyAnnotations. I guess this will be sorted out in later versions. Link to related source


The following code should do what you want.

class SomeTest : StringSpec({
    "getting annotation values" {
        val result = enumValues<Size>().map {
            it.declaringClass.getField(it.name).getAnnotation(JsonProperty::class.java).value
        }
        result shouldBe listOf("really-tall", "really-grande")
    }
})

An alternative (less code): Add a String property to your enum class (I called it someFieldName in the below code), annotate with @get:JsonValue, and construct each enum entry with the string value you want. @get:JsonValue will use someFieldName instead of the enum value during serialization.

enum class Size(@get:JsonValue val someFieldName: String) {
    TALL("really-tall"),
    GRANDE("really-grande");
}

Same test again

class SomeTest : StringSpec({
    "getting prop values" {
        val result = enumValues<Size>().map {
            it.someFieldName
        }
        result shouldBe listOf("really-tall", "really-grande")
    }
})

We're using the latter approach in an ongoing project.

Upvotes: 1

Related Questions