Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I deserialize JSON into a List<SomeType> with Kotlin + Jackson [duplicate]

What is the correct syntax to deserialize the following JSON:

[ {   "id" : "1",   "name" : "Blues" }, {   "id" : "0",   "name" : "Rock" } ] 

I tried:

//Works OK val dtos  = mapper.readValue(json, List::class.java) 

However I want:

val dtos : List<GenreDTO>  = mapper.readValue(json,      List<GenreDTO>::class.java) 

The above syntax is not correct and gives: only classes are allowed on the left hand side of a class literal

like image 708
Jasper Blues Avatar asked Jan 11 '16 08:01

Jasper Blues


People also ask

What is polymorphic deserialization?

A polymorphic deserialization allows a JSON payload to be deserialized into one of the known gadget classes that are documented in SubTypeValidator. java in jackson-databind in GitHub. The deserialized object is assigned to a generic base class in your object model, such as java. lang.

How does Jackson read nested JSON?

A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.

What is Jackson deserialization?

Jackson is a powerful and efficient Java library that handles the serialization and deserialization of Java objects and their JSON representations. It's one of the most widely used libraries for this task, and runs under the hood of many other frameworks.

What does Jackson module Kotlin do?

GitHub - FasterXML/jackson-module-kotlin: Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes. Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.


2 Answers

NOTE: The answer from @IRus is also correct, it was being modified at the same time I wrote this to fill in more details.

You should use the Jackson + Kotlin module or you will have other problems deserializing into Kotlin objects when you do no have a default constructor.

Your first sample of the code:

val dtos  = mapper.readValue(json, List::class.java) 

Is returning an inferred type of List<*> since you did not specify more type information, and it is actually a List<Map<String,Any>> which is not really "working OK" but is not producing any errors. It is unsafe, not typed.

The second code should be:

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.fasterxml.jackson.module.kotlin.readValue  val mapper = jacksonObjectMapper() // ... val genres: List<GenreDTO> = mapper.readValue(json) 

You do not need anything else on the right side of the assignment, the Kotlin module for Jackson will reify the generics and create the TypeReference for Jackson internally. Notice the readValue import, you need that or .* for the com.fasterxml.jackson.module.kotlin package to have the extension functions that do all of the magic.

A slightly different alternative that also works:

val genres = mapper.readValue<List<GenreDTO>>(json) 

There is no reason to NOT use the extension functions and the add-on module for Jackson. It is small and solves other issues that would require you to jump through hoops to make a default constructor, or use a bunch of annotations. With the module, your class can be normal Kotlin (optional to be data class):

class GenreDTO(val id: Int, val name: String) 
like image 157
3 revs Avatar answered Sep 20 '22 05:09

3 revs


The error you're getting is about following expression:

List<GenreDTO>::class.java 

Because of how jvm treats generics there's no separate class for List<GenreDTO> thus compiler complains. Similarly in Java the following will not compile:

List<GenreDTO>.getClass() 

Here's a sample that will deserialize the list properly:

val value:List<GenreDTO> = mapper.readValue(json, object : TypeReference<List<GenreDTO>>() {}) 

As @JaysonMinard has pointed out you can use jackson-module-kotlin to simplify the invocation to:

val genres: List<GenreDTO> = mapper.readValue(json) // or val genres = mapper.readValue<List<GenreDTO>>(json) 

This is possible because of reified type parameters. Consider looking at Extensions to find out details.

like image 37
miensol Avatar answered Sep 18 '22 05:09

miensol