How to use jackson to deserialize to Kotlin collections

TL;DR

With Jackson 2.6.3-2 do as @jason-minard advises and simply use:

import com.fasterxml.jackson.module.kotlin.readValue

val listOfD: List<D> = jacksonMapper.readValue(jsonStr)

Details

There's nothing special about Kotlin wrt to deserializing collections, although you'll need the kotlin jackson module to deserialize data classes without annotations.

Namely, it will require full reified type information in your case in order to keep track of the generic parameter of the list (D) ; otherwise (e.g. if you use readValue(jsonStr, List::class.java)) Jackson will only see it as an erased type (i.e. List) (as Kotlin makes explicit) and deserialize it to a List<Map<String, String>>, not knowing it has to construct Ds. This is worked around by using an anonymous subclass of TypeReference in Java so that Jackson can access the full reified type to deserialize to at run time.

Translating Jackson Java code literally to Kotlin, the following thus achieves what you want (and as commented by @eski, notice that the JSON in your example is invalid):

val jacksonMapper = ObjectMapper().registerModule(KotlinModule())

val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":2}]"""
val listOfD: List<D> = jacksonMapper.readValue(jsonStr, object : TypeReference<List<D>>(){})

assertEquals(listOf(D("value1", 1), D("value2", 2)), listOfD)

This is a bit verbose and ugly though, so you could hide it in a Kotlin (extension) function (especially if you plan on using it several times):

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

which would allow you to then just call:

val listOfD: List<D> = jacksonMapper.readValue(jsonStr)

And this is just what's included in the 2.6.3-2 jackson Kotlin module.


If you don't have the type of your generic content, like <MyData> in the example below:

val value = context.readValue<List<MyData>>(parser, valueType)

You can make it by implementing not only the JsonSerialzier but also the ContextualDeserializer like in the code sample below (based on this answer in Java):

class GenericListDeserializer : JsonDeserializer<List<*>>(), ContextualDeserializer {
    private var valueType: JavaType? = null

    override fun createContextual(ctxt: DeserializationContext, property: BeanProperty): JsonDeserializer<List<*>> {
        val wrapperType = property.type
        val valueType = wrapperType.containedType(0)
        val deserializer = GenericListDeserializer()
        deserializer.valueType = valueType
        return deserializer
    }

    override fun deserialize(parser: JsonParser, context: DeserializationContext): List<*> {
        val value :Any? = context.readValue(parser, valueType)
        return listOf(value)
    }
}

With Jackson Kotlin Module current versions, if you import the full module package or the specific extension function you'll have all extension methods available. Such as:

import com.fasterxml.jackson.module.kotlin.*  
val JSON = jacksonObjectMapper()  // keep around and re-use
val myList: List<String> = JSON.readValue("""["a","b","c"]""")

Therefore the Jackson Module for Kotlin will infer the the correct type and you do not need a TypeReference instance.

so your case (slightly renamed and fixed the data class, and JSON):

import com.fasterxml.jackson.module.kotlin.readValue

data class MyData(val a: String, val b: Int)
val JSON = jacksonObjectMapper()  

val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b": 2}]"""
val myList: List<MyData> = JSON.readValue(jsonStr)

You can also use the form:

val myList = JSON.readValue<List<MyData>>(jsonStr)

Without the import you will have an error because the extension function is not found.