0

I use the lib jackson-module-kotlin to parse string of json into object.

My issue is when I parse a string into an enum , and when I launch with intellij, I have this stack trace:

Caused by: kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Reflection on built-in Kotlin types is not yet fully supported. No metadata found for public final val name: kotlin.String defined in kotlin.Enum[DeserializedPropertyDescriptor@212b316a]

I don't have this issue when I launch with maven.

I use kotlin 1.1.51, with intellij kotlin plugin 1.2.0-release-IJ2017.3-1, I target a JVM 1.8, and i use jackson-module-kotlin version 2.8.7

what should I do?

 enum class CType { DEAL, FILE }
 data class Code(val code: String, val type: CType)

 fun testDeserialization() {
    val mapper = jacksonObjectMapper()
    // following line throws an exception:
    mapper.readValue("""{"code":"A","type":"DEAL"}""", Code::class.java)
 }
Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
sab
  • 4,352
  • 7
  • 36
  • 60

4 Answers4

5

The only way I got it working is by adding additional @JvmStatic annotation. I had mapper.registerModule(new KotlinModule()); and all, nothing worked but this:

package nc.features.algo.model

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonValue

enum class LHStatus (
  @get:JsonValue val code: Int
) {
  LH_POS_OVU_WAITING(1),
  LH_NEG_OVU_WAITING(2),
  ;

  companion object {
    @JsonCreator
    @JvmStatic
    fun deser(code: Int?): LHStatus? {
      if (code == null) return null
      for (i in values()) {
         if (i.code == code) return i
      }
      return null
    }
  }
}

Kirill Groshkov
  • 1,535
  • 1
  • 22
  • 23
2

You have to do a few things.

Update Jackson dependencies to the latest version (right now, 2.9.4). Update Kotlin version to a version equal or greater than 1.3.0.

Be sure to add the following dependencies to your build.gradle:

implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"

... then you call registerKotlinModule() on your Jackson ObjectMapper and the code of your enum should be just like this:

enum class CType(@get:JsonValue val value: String) {
    DEAL("deal"),
    FILE("file");

    companion object {

        @JsonCreator
        fun fromString(value: String): CType? {
            for (type in CType.values()) {
                if (type.name.equals(value, true)) {
                    return gender
                }
            }
            return null
        }
    }
}
andrea.rinaldi
  • 1,167
  • 1
  • 13
  • 33
0

Intellij is most likely using the kotlin compiler version 1.2.0 (from the plugin) and it doesn't seem to support reflection properly.

I suggest you do one of the following:

  1. Upgrade your kotlin version in maven and the intellij kotlin plugin to newer versions (e.g. 1.2.30). If you do that, you also have to update jackson-module-kotlin to >= 1.9, since there is an incompatibility with kotlin 1.2 (see here).

  2. Set the kotlin compiler version to 1.1 in Intellij Idea settings.

It is generally a good idea to use the same version of kotlin in Intellij Idea and maven/gradle.

0

You need to use the Kotlin module for Jackson that is compatible with Kotlin 1.2.x, this includes minimally these three versions of the module:

  • 2.9.4.1 (works with any 2.9.x of Jackson, but best to use most recent)
  • 2.8.11.1 (for Jackson 2.8.x)
  • 2.7.9.1 (for Jackson 2.7.x)

Otherwise, you will run into a problem with library mismatches.

The jackson-module-kotlin homepage lists these as the current versions, but they are likely to change and you can check the various Maven repository search engines to see which library versions are available and which dependencies they have on Kotlin to find matching versions.

Also note you can import the extensions for the ObjectMapper class and use reified types, so instead of:

val something = mapper.readValue("""{"code":"A","type":"DEAL"}""", Code::class.java)

you would have:

val something: Code = mapper.readValue("""{"code":"A","type":"DEAL"}""")

or alternatively:

val something = mapper.readValue<Code>("""{"code":"A","type":"DEAL"}""")

It is usually bad to use the erased type (i.e. Whatever::class.java) since this does not work for anything with generic type parameters, and using reified types also works nicely when deserializing into collections.

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227