Dániel Kis
Dániel Kis

Reputation: 2641

Kotlin: objectmapper.readValue() with TypeReference<HashMap<String, String>> cannot infer parameter

I would like to deserialize a json to Map with objectmapper with the following code:

fun onMessage(topic: String, message: MqttMessage) {
   val typeRef = object : TypeReference<HashMap<String, String>>() {}
   val msg = objectMapper.readValue(message.payload, typeRef)
   ...
}

Compiler says it connot infer parameter T in fun <T : Any!> readValue (src: ByteArray!, valueTypeRef: (TypeReference<Any!>..TypeReference<*>?)): T!

Is there any solution to this problem whitout extending a HashMap with my custom class like this:

class MyHashMap : HashMap<String, String>()

...

fun onMessage(topic: String, message: MqttMessage) {
   val msg = objectMapper.readValue(message.payload, MyHashMap::class.java)
   ...
}

Upvotes: 21

Views: 32578

Answers (3)

Kirill Simonov
Kirill Simonov

Reputation: 8491

You can use an object expression to pass an anonymous implementation of TypeReference:

objectMapper.readValue(message.payload, object : TypeReference<HashMap<String, String>>() {})

Notice the object keyword before the TypeReference parameter.

Upvotes: 3

zsmb13
zsmb13

Reputation: 89658

The issue, really, is in Jackson's API here. Here's how the readValue method is declared:

public <T> T readValue(String content, TypeReference valueTypeRef)

They are using the raw type of TypeReference for some reason, even though they could easily take a TypeReference<T> as their parameter. If they did, you code would work as is, as Kotlin could then infer the T generic type parameter of the function, and therefore know its return type.

You can work around this issue a couple different ways, however, by being explicit with your types.

  • Either by providing the generic type parameter for the function, and having the type of the msg variable inferred:

      val typeRef: TypeReference<Map<K, V>> = object : TypeReference<Map<K, V>>() {} 
      val msg = objectMapper.readValue<HashMap<String, String>>(message.payload, typeRef)
    
  • Or alternatively, by explicitly typing your variable, and having the function's type parameter inferred:

      val msg: HashMap<String, String> = objectMapper.readValue(message.payload, typeRef)
    

Upvotes: 19

rvit34
rvit34

Reputation: 2116

One possible way:

inline fun <reified T> ObjectMapper.readValue(s: String): T = this.readValue(s, object : TypeReference<T>() {})

val msg: Map<String,String> = objectMapper.readValue(message.payload) 

Upvotes: 8

Related Questions