How to use Jackson for deserialization in the collections of Kotlin

Example code that 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? 
+10
source share
3 answers

With the Jackson Kotlin module, current versions , if you import a complete module package or a specific extension function, you will have all the extension methods available. For instance:

 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 output the correct type and you will not need an instance of TypeReference .

so your case (slightly renamed and fixed 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 import, you will have an error because the extension function was not found.

+20
source

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) 

the details

There is nothing special about collection descriptorization in Kotlin, although you will need the kotlin jackson module to deserialize data classes without annotations.

Namely, you will need complete type information in your case in order to track the general list parameter ( D ); otherwise (for example, 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 deserializes it to List<Map<String, String>> , not knowing, this should build D s. This can be circumvented by using an anonymous subclass of TypeReference in Java so that Jackson can access the full reified type for deserialization at runtime.

Translating Jackson's Java code literally into Kotlin, you get the following what you want (and, as @eski commented, note that JSON is not valid in your example):

 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 unpleasant, so you can hide it in the Kotlin function (extension) (especially if you plan to use it several times):

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

which allows you to simply call:

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

And this is exactly what is included in 2.6.3-2 Kotlin's 2.6.3-2 module.

+10
source

If you don’t have a type for your shared content, for example <MyData> in the example below:

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

You can do this by implementing not only JsonSerialzier but also ContextualDeserializer as in the code example 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) } } 
0
source

Source: https://habr.com/ru/post/1234606/


All Articles