Sample code what I want:
data class D(val a: String, val b: Int)
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":"2}]"""
// what I need
val listOfD: List<D> = jacksonObjectMapper().whatMethodAndParameter?
Sample code what I want:
data class D(val a: String, val b: Int)
val jsonStr = """[{"a": "value1", "b": 1}, {"a": "value2", "b":"2}]"""
// what I need
val listOfD: List<D> = jacksonObjectMapper().whatMethodAndParameter?
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.
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)
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 D
s. 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)
}
}